├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cbio.c ├── cbio.h ├── client.c ├── server.c ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # certs 55 | *.pem 56 | *.srl 57 | 58 | # binary 59 | server 60 | client 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 stepheny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-g 2 | LDFLAGS=-lssl -lcrypto 3 | LIBS=cbio.o util.o 4 | 5 | .PHONY: all clean certs delete-certs 6 | 7 | all: server client certs 8 | 9 | server: server.c $(LIBS) 10 | cc -o $@ $^ $(CFLAGS) $(LDFLAGS) 11 | 12 | client: client.c $(LIBS) 13 | cc -o $@ $^ $(CFLAGS) $(LDFLAGS) -lreadline 14 | 15 | certs: root-key.pem root-ca.pem \ 16 | server-key.pem server-csr.pem server-cert.pem \ 17 | client-key.pem client-csr.pem client-cert.pem 18 | 19 | clean: delete-certs 20 | rm -f cbio.o util.o server client 21 | 22 | delete-certs: 23 | rm -f *.pem *.srl 24 | 25 | %-key.pem: 26 | openssl ecparam -name secp384r1 -genkey -noout -out $@ 27 | 28 | %-cert.pem: %-csr.pem root-ca.pem root-key.pem 29 | openssl x509 -req -in $< -out $@ -CA root-ca.pem -CAkey root-key.pem -days 7 30 | 31 | %-csr.pem: %-key.pem 32 | openssl req -new -key $< -out $@ -subj /CN=test_$*/ 33 | 34 | root-ca.pem: root-key.pem 35 | openssl req -new -x509 -nodes -days 7 -key $< -out $@ -subj /CN=test_rootCA/ 36 | test -f root-ca.srl || echo 00 > root-ca.srl 37 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openssl-dtls-custom-bio 2 | 3 | A simple dtls server client program implemented with openssl library. 4 | 5 | A custom bio is used to expose underlying bio ctrl and packet transmission. 6 | 7 | CA is used to verify server and client. 8 | 9 | UDP server and client. 10 | 11 | ### Prerequisite 12 | 13 | * a working c compiler and make 14 | * openssl with header files 15 | * readline library for client 16 | ``` 17 | apt-get install build-essential openssl libssl-dev libreadline-dev 18 | ``` 19 | 20 | ### Build 21 | 22 | ``` 23 | make 24 | ``` 25 | 26 | ### Run 27 | 28 | start server with a list of addresses to listen 29 | ``` 30 | ./server 127.0.0.1:1234 # listen on local loopback ipv4 port 1234 31 | ./server [::1]:1234 # listen on local loopback ipv6 port 1234 32 | ./server 127.0.0.1:1234 [::1]:1234 # listen on multiple port 33 | ./server [::]:1234 # listen on any interface both ipv4/ipv6 port 1234 34 | ``` 35 | 36 | start client with target server 37 | ``` 38 | ./client 127.0.0.1:1234 # connect to local server on port 1234 39 | ``` 40 | 41 | once connected, some simple commands could be issued from client terminal 42 | * `ping` returns `pong` 43 | * `echo ` returns `` 44 | * `whoami` returns client's address and port seen by server 45 | * `stats` returns a list of server currently serving clients 46 | * `bc ` broadcast `` to all clients 47 | 48 | ctrl-d could be used to stop client 49 | 50 | ctrl-c could be used to stop server or client 51 | 52 | certs can be regenerated by 53 | ``` 54 | make delete-certs 55 | make certs 56 | ``` 57 | 58 | ### Bugs 59 | 60 | The commands used in Makefile to generate certificates are not supposed to be 61 | good practice, test use only. 62 | 63 | A 2000 byte buffer size is hardcoded in program, an approaching sized message 64 | would fail to be sent or received. This is program's bug, neither DTLS nor UDP 65 | has this limitation. Although packet fragmentation should be avoided. 66 | 67 | For simplicity, SSL timeout controls on bio were ignored. Although openssl 68 | library would automatically handle state machine, (in this program) this is only 69 | triggered on write or received packet events. Resulting 70 | * a long polling receiver might fail to receive new message until next write 71 | (requiring periodical read write to trigger state machine update) 72 | * a client failed to notify shutdown would left on server's list forever 73 | (missing dead peer detection) 74 | 75 | ### Pitfalls 76 | 77 | `[::]` would listen on both ipv6 and ipv4, so it would conflict with other 78 | addresses with same port, even ipv4 addresses. 79 | 80 | Although the built executables could work with different version of openssl 81 | library, it is recommended to rebuild in different environment. For example, 82 | openssl changed the value of macro defined const `BIO_CTRL_DGRAM_SET_PEEK_MODE` 83 | between 1.1.0f and 1.1.0g, this would "not" break ABI compatability but 84 | definitely would let related function misbehave. 85 | -------------------------------------------------------------------------------- /cbio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "util.h" 11 | #include "cbio.h" 12 | 13 | // #define fprintf(...) 14 | 15 | int BIO_s_custom_write_ex(BIO *b, const char *data, size_t dlen, size_t *written) 16 | { 17 | fprintf(stderr, "BIO_s_custom_write_ex(BIO[0x%016lX], data[0x%016lX], dlen[%ld], *written[%ld])\n", b, data, dlen, *written); 18 | fflush(stderr); 19 | 20 | return -1; 21 | } 22 | 23 | int BIO_s_custom_write(BIO *b, const char *data, int dlen) 24 | { 25 | int ret; 26 | custom_bio_data_t *cdp; 27 | 28 | ret = -1; 29 | fprintf(stderr, "BIO_s_custom_write(BIO[0x%016lX], buf[0x%016lX], dlen[%ld])\n", b, data, dlen); 30 | fflush(stderr); 31 | cdp = (custom_bio_data_t *)BIO_get_data(b); 32 | 33 | dump_addr((struct sockaddr *)&cdp->txaddr, ">> "); 34 | // dump_hex((unsigned const char *)data, dlen, " "); 35 | ret = sendto(cdp->txfd, data, dlen, 0, (struct sockaddr *)&cdp->txaddr, cdp->txaddr_buf.len); 36 | if (ret >= 0) 37 | fprintf(stderr, " %d bytes sent\n", ret); 38 | else 39 | fprintf(stderr, " ret: %d errno: [%d] %s\n", ret, errno, strerror(errno)); 40 | 41 | return ret; 42 | } 43 | 44 | int BIO_s_custom_read_ex(BIO *b, char *data, size_t dlen, size_t *readbytes) 45 | { 46 | fprintf(stderr, "BIO_s_custom_read_ex(BIO[0x%016lX], data[0x%016lX], dlen[%ld], *readbytes[%ld])\n", b, data, dlen, *readbytes); 47 | fflush(stderr); 48 | 49 | return -1; 50 | } 51 | 52 | int BIO_s_custom_read(BIO *b, char *data, int dlen) 53 | { 54 | int ret; 55 | custom_bio_data_t *cdp; 56 | deque_t *dp; 57 | buffer_t *bp; 58 | 59 | ret = -1; 60 | fprintf(stderr, "BIO_s_custom_read(BIO[0x%016lX], data[0x%016lX], dlen[%ld])\n", b, data, dlen); 61 | fprintf(stderr, " probe peekmode %d\n", 62 | ((custom_bio_data_t *)BIO_get_data(b))->peekmode); 63 | fflush(stderr); 64 | 65 | cdp = (custom_bio_data_t *)BIO_get_data(b); 66 | dp = &cdp->rxqueue; 67 | fprintf(stderr, " data[0x%016lX] queue: %d\n", dp, deque_count(dp)); 68 | if (dp->head) 69 | { 70 | if (((custom_bio_data_t *)BIO_get_data(b))->peekmode) 71 | bp = (buffer_t *)deque_peekleft(dp); 72 | else 73 | bp = (buffer_t *)deque_popleft(dp); 74 | fprintf(stderr, " buf[0x%016lX]\n", bp); 75 | fflush(stderr); 76 | 77 | ret = (bp->len<=dlen) ? bp->len : dlen; 78 | memmove(data, bp->buf, ret); 79 | 80 | if (!((custom_bio_data_t *)BIO_get_data(b))->peekmode) 81 | buffer_free(bp); 82 | } 83 | 84 | return ret; 85 | } 86 | 87 | int BIO_s_custom_gets(BIO *b, char *data, int size); 88 | 89 | int BIO_s_custom_puts(BIO *b, const char *data); 90 | 91 | 92 | long BIO_s_custom_ctrl(BIO *b, int cmd, long larg, void *pargs) 93 | { 94 | long ret = 0; 95 | 96 | // fprintf(stderr, "BIO_s_custom_ctrl(BIO[0x%016lX], cmd[%d], larg[%ld], pargs[0x%016lX])\n", b, cmd, larg, pargs); 97 | fflush(stderr); 98 | 99 | switch(cmd) 100 | { 101 | case BIO_CTRL_FLUSH: // 11 102 | case BIO_CTRL_DGRAM_SET_CONNECTED: // 32 103 | case BIO_CTRL_DGRAM_SET_PEER: // 44 104 | case BIO_CTRL_DGRAM_GET_PEER: // 46 105 | ret = 1; 106 | break; 107 | case BIO_CTRL_WPENDING: // 13 108 | ret = 0; 109 | break; 110 | case BIO_CTRL_DGRAM_QUERY_MTU: // 40 111 | case BIO_CTRL_DGRAM_GET_FALLBACK_MTU: // 47 112 | ret = 1500; 113 | // ret = 9000; // jumbo? 114 | break; 115 | case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD: // 49 116 | ret = 96; // random guess 117 | break; 118 | case BIO_CTRL_DGRAM_SET_PEEK_MODE: // 71 119 | ((custom_bio_data_t *)BIO_get_data(b))->peekmode = !!larg; 120 | ret = 1; 121 | break; 122 | case BIO_CTRL_PUSH: // 6 123 | case BIO_CTRL_POP: // 7 124 | case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: // 45 125 | ret = 0; 126 | break; 127 | default: 128 | fprintf(stderr, "BIO_s_custom_ctrl(BIO[0x%016lX], cmd[%d], larg[%ld], pargs[0x%016lX])\n", b, cmd, larg, pargs); 129 | fprintf(stderr, " unknown cmd: %d\n", cmd); 130 | fflush(stderr); 131 | ret = 0; 132 | raise(SIGTRAP); 133 | break; 134 | } 135 | 136 | return ret; 137 | } 138 | 139 | int BIO_s_custom_create(BIO *b) 140 | { 141 | fprintf(stderr, "BIO_s_custom_create(BIO[0x%016lX])\n", b); 142 | fflush(stderr); 143 | 144 | return 1; 145 | } 146 | 147 | int BIO_s_custom_destroy(BIO *b) 148 | { 149 | fprintf(stderr, "BIO_s_custom_destroy(BIO[0x%016lX])\n", b); 150 | fflush(stderr); 151 | 152 | return 1; 153 | } 154 | 155 | // long BIO_s_custom_callback_ctrl(BIO *, int, BIO_info_cb *); 156 | 157 | BIO_METHOD *_BIO_s_custom = NULL; 158 | BIO_METHOD *BIO_s_custom(void) 159 | { 160 | if (_BIO_s_custom) 161 | return _BIO_s_custom; 162 | 163 | _BIO_s_custom = BIO_meth_new(BIO_get_new_index()|BIO_TYPE_SOURCE_SINK, "BIO_s_custom"); 164 | 165 | // BIO_meth_set_write_ex(_BIO_s_custom, BIO_s_custom_write_ex); 166 | BIO_meth_set_write(_BIO_s_custom, BIO_s_custom_write); 167 | // BIO_meth_set_read_ex(_BIO_s_custom, BIO_s_custom_read_ex); 168 | BIO_meth_set_read(_BIO_s_custom, BIO_s_custom_read); 169 | BIO_meth_set_ctrl(_BIO_s_custom, BIO_s_custom_ctrl); 170 | BIO_meth_set_create(_BIO_s_custom, BIO_s_custom_create); 171 | BIO_meth_set_destroy(_BIO_s_custom, BIO_s_custom_destroy); 172 | // BIO_meth_set_callback_ctrl(_BIO_s_custom, BIO_s_custom_callback_ctrl); 173 | 174 | return _BIO_s_custom; 175 | } 176 | 177 | void BIO_s_custom_meth_free(void) 178 | { 179 | if (_BIO_s_custom) 180 | BIO_meth_free(_BIO_s_custom); 181 | 182 | _BIO_s_custom = NULL; 183 | } 184 | 185 | #undef fprintf 186 | -------------------------------------------------------------------------------- /cbio.h: -------------------------------------------------------------------------------- 1 | #ifndef CBIO_H 2 | #define CBIO_H 3 | 4 | typedef struct custom_bio_data_st { 5 | buffer_t txaddr_buf; 6 | // struct sockaddr txaddr; 7 | struct sockaddr_storage txaddr; 8 | deque_t rxqueue; 9 | int txfd; 10 | int peekmode; 11 | } custom_bio_data_t; 12 | 13 | int BIO_s_custom_write_ex(BIO *b, const char *data, size_t dlen, size_t *written); 14 | int BIO_s_custom_write(BIO *b, const char *data, int dlen); 15 | int BIO_s_custom_read_ex(BIO *b, char *data, size_t dlen, size_t *readbytes); 16 | int BIO_s_custom_read(BIO *b, char *data, int dlen); 17 | int BIO_s_custom_gets(BIO *b, char *data, int size); 18 | int BIO_s_custom_puts(BIO *b, const char *data); 19 | long BIO_s_custom_ctrl(BIO *b, int cmd, long larg, void *pargs); 20 | int BIO_s_custom_create(BIO *b); 21 | int BIO_s_custom_destroy(BIO *b); 22 | // long BIO_s_custom_callback_ctrl(BIO *, int, BIO_info_cb *); 23 | 24 | BIO_METHOD *BIO_s_custom(void); 25 | void BIO_s_custom_meth_free(void); 26 | 27 | #endif /* CBIO_H */ 28 | -------------------------------------------------------------------------------- /client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "util.h" 18 | #include "cbio.h" 19 | 20 | enum 21 | { 22 | TIME_OUT = 8000 // ms 23 | }; 24 | 25 | void signal_handler(int sig) 26 | { 27 | if (sig==SIGINT) 28 | fprintf(stderr, "Interrupt from keyboard\n"); 29 | else 30 | fprintf(stderr, "unknown signal[%d]\n", sig); 31 | fflush(stderr); 32 | } 33 | 34 | SSL *ssl; 35 | 36 | int run = 1; 37 | int connected = 0; 38 | 39 | void readline_handler(char *line) 40 | { 41 | if (line) 42 | { 43 | if (connected) 44 | SSL_write(ssl, line, strlen(line)); 45 | else 46 | fputs("not connected\n", stderr); 47 | free(line); 48 | } 49 | else 50 | { 51 | fprintf(stderr, "^D\n"); 52 | run = 0; 53 | if (connected) 54 | SSL_shutdown(ssl); 55 | } 56 | } 57 | 58 | int main(int argc, char **argv) 59 | { 60 | int ret; 61 | 62 | if (argc!=2) 63 | { 64 | fputs("usage:\n" 65 | " client 127.0.0.1:1234\n" 66 | " client [::1]:1234\n", stderr); 67 | 68 | exit(0); 69 | } 70 | 71 | custom_bio_data_t cbio_data; 72 | char *c; 73 | int p; 74 | 75 | if (argv[1][0]=='[') 76 | { 77 | c = strchr(argv[1], ']'); 78 | if (!c) 79 | { 80 | fputs("invalid target: ", stderr); 81 | fputs(argv[1], stderr); 82 | fputc('\n', stderr); 83 | 84 | exit(1); 85 | } 86 | p = atoi(c+2); 87 | if (p<1||p>65535) 88 | { 89 | fputs("invalid port: ", stderr); 90 | fputs(argv[1], stderr); 91 | fputc('\n', stderr); 92 | 93 | exit(1); 94 | } 95 | *c = '\0'; 96 | 97 | cbio_data.txaddr.ss_family = AF_INET6; 98 | 99 | ret = inet_pton(AF_INET6, argv[1]+1, &((struct sockaddr_in6 *)&cbio_data.txaddr)->sin6_addr); 100 | if (!ret) 101 | { 102 | fputs("invalid ipv6 address: ", stderr); 103 | fputs(argv[1], stderr); 104 | fputc('\n', stderr); 105 | 106 | exit(1); 107 | } 108 | ((struct sockaddr_in6 *)&cbio_data.txaddr)->sin6_port = htons(p); 109 | cbio_data.txaddr_buf.cap = sizeof(cbio_data.txaddr); 110 | cbio_data.txaddr_buf.len = sizeof(struct sockaddr_in6); 111 | } 112 | else 113 | { 114 | c = strchr(argv[1], ':'); 115 | if (!c) 116 | { 117 | fputs("invalid target: ", stderr); 118 | fputs(argv[1], stderr); 119 | fputc('\n', stderr); 120 | 121 | exit(1); 122 | } 123 | p = atoi(c+1); 124 | if (p<1||p>65535) 125 | { 126 | fputs("invalid port: ", stderr); 127 | fputs(argv[1], stderr); 128 | fputc('\n', stderr); 129 | 130 | exit(1); 131 | } 132 | *c = '\0'; 133 | 134 | cbio_data.txaddr.ss_family = AF_INET; 135 | 136 | ret = inet_pton(AF_INET, argv[1], &((struct sockaddr_in *)&cbio_data.txaddr)->sin_addr); 137 | if (!ret) 138 | { 139 | fputs("invalid ipv4 address: ", stderr); 140 | fputs(argv[1], stderr); 141 | fputc('\n', stderr); 142 | 143 | exit(1); 144 | } 145 | ((struct sockaddr_in *)&cbio_data.txaddr)->sin_port = htons(p); 146 | cbio_data.txaddr_buf.cap = sizeof(cbio_data.txaddr); 147 | cbio_data.txaddr_buf.len = sizeof(struct sockaddr_in); 148 | } 149 | 150 | int sockfd = socket(cbio_data.txaddr.ss_family, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); 151 | 152 | if (connect(sockfd, (struct sockaddr *)&cbio_data.txaddr, cbio_data.txaddr_buf.len)) 153 | { 154 | fputs("failed to connect\n", stderr); 155 | 156 | exit(1); 157 | } 158 | cbio_data.txfd = sockfd; 159 | 160 | assert(sockfd); 161 | deque_init(&cbio_data.rxqueue); 162 | cbio_data.peekmode = 0; 163 | 164 | int epfd = epoll_create1(EPOLL_CLOEXEC); 165 | struct epoll_event epe = {0}; 166 | 167 | epe.data.fd = fileno(stdin); 168 | epe.events = EPOLLIN; 169 | 170 | epoll_ctl(epfd, EPOLL_CTL_ADD, epe.data.fd, &epe); 171 | 172 | fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL) | O_NONBLOCK); 173 | 174 | SSL_load_error_strings(); 175 | SSL_library_init(); 176 | 177 | SSL_CTX *ctx = SSL_CTX_new(DTLS_client_method()); 178 | SSL_CTX_set_min_proto_version(ctx, DTLS1_2_VERSION); 179 | SSL_CTX_use_certificate_chain_file(ctx, "client-cert.pem"); 180 | SSL_CTX_use_PrivateKey_file(ctx, "client-key.pem", SSL_FILETYPE_PEM); 181 | ret = SSL_CTX_load_verify_locations(ctx, "root-ca.pem", NULL); 182 | fprintf(stderr, "SSL_CTX_load_verify_locations -> %d\n", ret); 183 | ret = SSL_CTX_set_default_verify_file(ctx); 184 | fprintf(stderr, "SSL_CTX_set_default_verify_file -> %d\n", ret); 185 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); 186 | 187 | ssl = SSL_new(ctx); 188 | 189 | BIO *custom_bio = BIO_new(BIO_s_custom()); 190 | BIO_set_data(custom_bio, (void *)&cbio_data); 191 | BIO_set_init(custom_bio, 1); 192 | SSL_set_bio(ssl, custom_bio, custom_bio); 193 | 194 | epe.data.fd = sockfd; 195 | epe.events = EPOLLIN|EPOLLET; 196 | 197 | epoll_ctl(epfd, EPOLL_CTL_ADD, epe.data.fd, &epe); 198 | 199 | signal(SIGINT, signal_handler); 200 | 201 | buffer_t *packet; 202 | packet = buffer_new(2000); 203 | 204 | ret = SSL_connect(ssl); 205 | if (ret==1) 206 | { 207 | connected = 1; 208 | fputs("connected\n", stderr); 209 | } 210 | else if (SSL_get_error(ssl, ret)==SSL_ERROR_SSL) 211 | { 212 | dump_addr((struct sockaddr *)&cbio_data.txaddr, "ssl error: "); 213 | ERR_print_errors_fp(stderr); 214 | } 215 | 216 | rl_callback_handler_install(">> ", readline_handler); 217 | 218 | while(run) 219 | { 220 | ret = epoll_wait(epfd, &epe, 1, TIME_OUT); 221 | if (ret<0) 222 | { 223 | if (connected) 224 | SSL_shutdown(ssl); 225 | 226 | break; 227 | } 228 | else if (ret==0) // time out 229 | continue; 230 | 231 | if (epe.data.fd==fileno(stdin)) 232 | rl_callback_read_char(); 233 | if (epe.data.fd==sockfd) 234 | { 235 | while ((packet->len=recv(sockfd, packet->buf, packet->cap, 0))>=0) 236 | { 237 | fprintf(stderr, "\033[2K\r<< %d bytes\n", packet->len); 238 | 239 | deque_append(&cbio_data.rxqueue, packet); 240 | 241 | packet = buffer_new(2000); 242 | 243 | if (connected) 244 | { 245 | packet->len = SSL_read(ssl, packet->buf, packet->cap); 246 | 247 | if (packet->len>0) 248 | { 249 | packet->buf[packet->len] = '\0'; 250 | printf("RECV: %s\n", packet->buf); 251 | } 252 | else if (packet->len==0) 253 | { 254 | SSL_shutdown(ssl); 255 | run = 0; 256 | } 257 | } 258 | else 259 | { 260 | ret = SSL_connect(ssl); 261 | 262 | if (ret==1) 263 | { 264 | connected = 1; 265 | fputs("connected\n", stderr); 266 | } 267 | else if (SSL_get_error(ssl, ret)==SSL_ERROR_SSL) 268 | { 269 | dump_addr((struct sockaddr *)&cbio_data.txaddr, "ssl error: "); 270 | ERR_print_errors_fp(stderr); 271 | 272 | run = 0; 273 | break; 274 | } 275 | } 276 | rl_forced_update_display(); 277 | } 278 | } 279 | } 280 | buffer_free(packet); 281 | 282 | deque_deinit(&cbio_data.rxqueue); 283 | 284 | SSL_free(ssl); 285 | SSL_CTX_free(ctx); 286 | 287 | BIO_s_custom_meth_free(); 288 | 289 | rl_cleanup_after_signal(); 290 | fputc('\n', stderr); 291 | 292 | close(sockfd); 293 | 294 | rl_callback_handler_remove(); 295 | 296 | return 0; 297 | } 298 | -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "util.h" 14 | #include "cbio.h" 15 | 16 | enum 17 | { 18 | TIME_OUT = 8000 // ms 19 | }; 20 | 21 | hashtable_t *ht; 22 | 23 | typedef struct client_s client_t; 24 | struct client_s 25 | { 26 | custom_bio_data_t data; 27 | SSL *ssl; 28 | void (*serve)(client_t *cli); 29 | }; 30 | 31 | char cookie_str[] = "BISCUIT!"; 32 | 33 | void on_connect(client_t *cli); 34 | void on_message(client_t *cli); 35 | 36 | void on_connect(client_t *cli) 37 | { 38 | int ret = SSL_accept(cli->ssl); 39 | fprintf(stderr, "SSL_accept -> %d\n", ret); 40 | int tmp; 41 | 42 | if (ret==1) 43 | { 44 | char buf[64]; 45 | int n; 46 | 47 | dump_addr((struct sockaddr *)&cli->data.txaddr, "user connected: "); 48 | cli->serve = on_message; 49 | n = snprintf(buf, sizeof(buf), "hello, %s", sdump_addr((struct sockaddr *)&cli->data.txaddr)); 50 | SSL_write(cli->ssl, buf, n); 51 | } 52 | else if ((tmp=SSL_get_error(cli->ssl, ret))==SSL_ERROR_SSL) 53 | { 54 | fprintf(stderr, "!!!! SSL_get_error -> %d\n", tmp); 55 | 56 | dump_addr((struct sockaddr *)&cli->data.txaddr, "ssl error: "); 57 | ERR_print_errors_fp(stderr); 58 | 59 | assert(ht_delete(ht, &cli->data.txaddr_buf)); 60 | SSL_free(cli->ssl); 61 | free(cli); 62 | } 63 | else 64 | { 65 | fprintf(stderr, "!!!! SSL_get_error -> %d\n", tmp); 66 | } 67 | } 68 | 69 | void on_message(client_t *cli) 70 | { 71 | char buf[2000]; 72 | int n; 73 | 74 | n = SSL_read(cli->ssl, buf, sizeof(buf)); 75 | 76 | fprintf(stderr, "SSL_read -> %d\n", n); 77 | fflush(stderr); 78 | 79 | if (n==0) 80 | { 81 | SSL_shutdown(cli->ssl); 82 | dump_addr((struct sockaddr *)&cli->data.txaddr, "|| "); 83 | assert(ht_delete(ht, &cli->data.txaddr_buf)); 84 | SSL_free(cli->ssl); 85 | free(cli); 86 | } 87 | else if (n>0) 88 | { 89 | if (n==6 && strncmp(buf, "whoami", 6)==0) 90 | { 91 | const char *tmp = sdump_addr((struct sockaddr *)&cli->data.txaddr); 92 | SSL_write(cli->ssl, tmp, strlen(tmp)); 93 | } 94 | else if (n==4 && strncmp(buf, "ping", 4)==0) 95 | SSL_write(cli->ssl, "pong", 4); 96 | else if (n>=5 && strncmp(buf, "echo ", 5)==0) 97 | SSL_write(cli->ssl, buf+5, n-5); 98 | else if (n==5 && strncmp(buf, "stats", 5)==0) 99 | { 100 | n = snprintf(buf, sizeof(buf), "users:"); 101 | HT_FOREACH(i, ht) 102 | { 103 | n += snprintf(buf+n, sizeof(buf)-n, "\n%s", sdump_addr((struct sockaddr *)&((client_t *)i->value)->data.txaddr)); 104 | } 105 | 106 | SSL_write(cli->ssl, buf, n); 107 | } 108 | else if (n>=3 && strncmp(buf, "bc ", 3)==0) 109 | { 110 | HT_FOREACH(i, ht) 111 | { 112 | SSL_write(((client_t *)i->value)->ssl, buf+3, n-3); 113 | } 114 | } 115 | } 116 | } 117 | 118 | int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) 119 | { 120 | memmove(cookie, cookie_str, sizeof(cookie_str)-1); 121 | *cookie_len = sizeof(cookie_str)-1; 122 | 123 | return 1; 124 | } 125 | 126 | int verify_cookie(SSL *ssl, const unsigned char *cookie, unsigned int cookie_len) 127 | { 128 | return sizeof(cookie_str)-1==cookie_len && memcmp(cookie, cookie_str, sizeof(cookie_str)-1)==0; 129 | } 130 | 131 | void signal_handler(int sig) 132 | { 133 | if (sig==SIGINT) 134 | fprintf(stderr, "Interrupt from keyboard\n"); 135 | else 136 | fprintf(stderr, "unknown signal[%d]\n", sig); 137 | fflush(stderr); 138 | } 139 | 140 | int main(int argc, char **argv) 141 | { 142 | int ret; 143 | 144 | if (argc<=1) 145 | { 146 | fputs("usage:\n" 147 | " server 127.0.0.1:1234\n" 148 | " server 0.0.0.0:1234\n" 149 | " server [::1]:1234\n" 150 | " server [::]:1234\n" 151 | " server [::]:1234 127.0.0.1:1234\n", stderr); 152 | 153 | exit(0); 154 | } 155 | 156 | deque_t *addrlist = deque_new(); 157 | for (int i=1; i65535) 173 | continue; 174 | *c = '\0'; 175 | 176 | bp = buffer_new(sizeof(struct sockaddr_in6)); 177 | bp->len = sizeof(struct sockaddr_in6); 178 | memset(bp->buf, 0, sizeof(struct sockaddr_in6)); 179 | ((struct sockaddr_in6 *)bp->buf)->sin6_family = AF_INET6; 180 | 181 | ret = inet_pton(AF_INET6, argv[i]+1, &((struct sockaddr_in6 *)bp->buf)->sin6_addr); 182 | if (!ret) 183 | { 184 | buffer_free(bp); 185 | continue; 186 | } 187 | ((struct sockaddr_in6 *)bp->buf)->sin6_port = htons(p); 188 | deque_append(addrlist, bp); 189 | } 190 | else 191 | { 192 | c = strchr(argv[i], ':'); 193 | if (!c) 194 | continue; 195 | p = atoi(c+1); 196 | if (p<1||p>65535) 197 | continue; 198 | *c = '\0'; 199 | 200 | bp = buffer_new(sizeof(struct sockaddr_in)); 201 | bp->len = sizeof(struct sockaddr_in); 202 | memset(bp->buf, 0, sizeof(struct sockaddr_in)); 203 | ((struct sockaddr_in *)bp->buf)->sin_family = AF_INET; 204 | 205 | ret = inet_pton(AF_INET, argv[i], &((struct sockaddr_in *)bp->buf)->sin_addr); 206 | if (!ret) 207 | { 208 | buffer_free(bp); 209 | continue; 210 | } 211 | ((struct sockaddr_in *)bp->buf)->sin_port = htons(p); 212 | deque_append(addrlist, bp); 213 | } 214 | 215 | } 216 | 217 | SSL_load_error_strings(); 218 | SSL_library_init(); 219 | 220 | const SSL_METHOD *mtd = DTLS_server_method(); 221 | SSL_CTX *ctx = SSL_CTX_new(mtd); 222 | SSL_CTX_set_min_proto_version(ctx, DTLS1_2_VERSION); 223 | SSL_CTX_use_certificate_chain_file(ctx, "server-cert.pem"); 224 | SSL_CTX_use_PrivateKey_file(ctx, "server-key.pem", SSL_FILETYPE_PEM); 225 | ret = SSL_CTX_load_verify_locations(ctx, "root-ca.pem", NULL); 226 | fprintf(stderr, "SSL_CTX_load_verify_locations -> %d\n", ret); 227 | ret = SSL_CTX_set_default_verify_file(ctx); 228 | fprintf(stderr, "SSL_CTX_set_default_verify_file -> %d\n", ret); 229 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); 230 | 231 | SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie); 232 | SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie); 233 | 234 | int epfd = epoll_create1(EPOLL_CLOEXEC); 235 | int run = 0; 236 | struct epoll_event epe = {0}; 237 | 238 | DEQUE_FOREACH(i, addrlist) 239 | { 240 | buffer_t *bp = (buffer_t *)i->p; 241 | assert(bp->len > 4); 242 | 243 | epe.data.fd = socket(((struct sockaddr *)bp->buf)->sa_family, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); 244 | 245 | fprintf(stderr, "new socket fd: %d\n", epe.data.fd); 246 | dump_addr((struct sockaddr *)bp->buf, "try bind: "); 247 | assert(bind(epe.data.fd, (struct sockaddr *)bp->buf, (socklen_t)bp->len) == 0); 248 | 249 | epe.events = EPOLLIN|EPOLLET; 250 | epoll_ctl(epfd, EPOLL_CTL_ADD, epe.data.fd, &epe); 251 | 252 | run = 1; 253 | } 254 | 255 | signal(SIGINT, signal_handler); 256 | 257 | ht = ht256_new(); 258 | 259 | client_t *client = (client_t *)malloc(sizeof(client_t)); 260 | client->ssl = SSL_new(ctx); 261 | deque_init(&client->data.rxqueue); 262 | client->data.txaddr_buf.cap = sizeof(struct sockaddr_storage); 263 | client->data.txaddr_buf.len = sizeof(struct sockaddr_storage); 264 | memset(&client->data.txaddr, 0, sizeof(struct sockaddr_storage)); 265 | client->data.peekmode = 0; 266 | client->serve = on_connect; 267 | 268 | BIO *bio = BIO_new(BIO_s_custom()); 269 | BIO_set_data(bio, (void *)&client->data); 270 | BIO_set_init(bio, 1); 271 | SSL_set_bio(client->ssl, bio, bio); 272 | 273 | buffer_t *packet; 274 | packet = buffer_new(2000); 275 | 276 | int new_line = 1; 277 | 278 | while (run) 279 | { 280 | ret = epoll_wait(epfd, &epe, 1, TIME_OUT); 281 | 282 | if (ret==-1) 283 | break; 284 | else if (ret==0) 285 | { 286 | time_t curtime; 287 | time(&curtime); 288 | char *tmp = ctime(&curtime); 289 | tmp[strlen(tmp)-1] = '\0'; 290 | fprintf(stderr, "wall time: %s\r", tmp); 291 | new_line = 1; 292 | 293 | // HT_FOREACH(i, ht) 294 | // { 295 | // SSL_write(((client_t *)i->value)->ssl, "tick", 4); 296 | // } 297 | 298 | continue; 299 | } 300 | 301 | if (new_line) 302 | { 303 | fputc('\n', stderr); 304 | new_line = 0; 305 | } 306 | 307 | while ((packet->len = recvfrom(epe.data.fd, packet->buf, packet->cap, 0, (struct sockaddr *)&client->data.txaddr, (socklen_t *)&client->data.txaddr_buf.len))>0) 308 | { 309 | dump_addr((struct sockaddr *)&client->data.txaddr, "<< "); 310 | 311 | client_t *cli = (client_t *)ht_search(ht, &client->data.txaddr_buf); 312 | if (cli) 313 | { 314 | deque_append(&cli->data.rxqueue, packet); 315 | cli->serve(cli); 316 | } 317 | else 318 | { 319 | client->data.txfd = epe.data.fd; 320 | deque_append(&client->data.rxqueue, packet); 321 | ret = DTLSv1_listen(client->ssl, NULL); 322 | fprintf(stderr, "DTLSv1_listen -> %d\n", ret); 323 | fflush(stderr); 324 | 325 | if (ret==1) 326 | { 327 | buffer_t *key = &client->data.txaddr_buf; 328 | ht_insert(ht, key, client); 329 | dump_addr((struct sockaddr *)&client->data.txaddr, "++ "); 330 | client->serve(client); 331 | 332 | 333 | client = (client_t *)malloc(sizeof(client_t)); 334 | client->ssl = SSL_new(ctx); 335 | deque_init(&client->data.rxqueue); 336 | client->data.txaddr_buf.cap = sizeof(struct sockaddr_storage); 337 | client->data.txaddr_buf.len = sizeof(struct sockaddr_storage); 338 | memset(&client->data.txaddr, 0, sizeof(struct sockaddr_storage)); 339 | client->data.peekmode = 0; 340 | client->serve = on_connect; 341 | 342 | BIO *bio = BIO_new(BIO_s_custom()); 343 | BIO_set_data(bio, (void *)&client->data); 344 | BIO_set_init(bio, 1); 345 | SSL_set_bio(client->ssl, bio, bio); 346 | 347 | } 348 | } 349 | 350 | packet = buffer_new(2000); 351 | } 352 | } 353 | 354 | buffer_free(packet); 355 | 356 | SSL_free(client->ssl); 357 | free(client); 358 | 359 | HT_FOREACH(i, ht) 360 | { 361 | SSL_shutdown(((client_t *)i->value)->ssl); 362 | dump_addr((struct sockaddr *)&((client_t *)i->value)->data.txaddr, "|| "); 363 | SSL_free(((client_t *)i->value)->ssl); 364 | free((client_t *)i->value); 365 | } 366 | ht_free(ht); 367 | 368 | BIO_s_custom_meth_free(); 369 | 370 | SSL_CTX_free(ctx); 371 | 372 | DEQUE_FOREACH(i, addrlist) 373 | buffer_free((buffer_t *)i->p); 374 | 375 | deque_free(addrlist); 376 | 377 | return 0; 378 | } 379 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "util.h" 9 | 10 | typedef union access_u 11 | { 12 | uint_fast64_t u64; 13 | uint32_t u32[2]; 14 | uint16_t u16[4]; 15 | uint8_t u8[8]; 16 | } access_t; 17 | 18 | deque_t *deque_new(void) 19 | { 20 | deque_t *dp = (deque_t *)malloc(sizeof(deque_t)); 21 | assert(dp); 22 | deque_init(dp); 23 | 24 | return dp; 25 | } 26 | 27 | void deque_init(deque_t *dp) 28 | { 29 | dp->head = NULL; 30 | dp->tail = NULL; 31 | } 32 | 33 | void deque_deinit(deque_t *dp) 34 | { 35 | assert(dp); 36 | while (dp->tail) 37 | deque_pop(dp); 38 | } 39 | 40 | void deque_free(deque_t *dp) 41 | { 42 | assert(dp); 43 | deque_deinit(dp); 44 | free(dp); 45 | } 46 | 47 | size_t deque_count(deque_t *dp) 48 | { 49 | assert(dp); 50 | size_t n = 0; 51 | 52 | for(deque_item_t *i=dp->head; i; i=i->next) 53 | ++n; 54 | 55 | return n; 56 | } 57 | 58 | void deque_append(deque_t *dp, void *p) 59 | { 60 | assert(dp); 61 | deque_item_t *i = (deque_item_t *)malloc(sizeof(deque_item_t)); 62 | assert(i); 63 | 64 | i->p = p; 65 | i->next = NULL; 66 | i->prev = dp->tail; 67 | 68 | if (dp->tail) 69 | dp->tail->next = i; 70 | else 71 | dp->head = i; 72 | dp->tail = i; 73 | } 74 | 75 | void *deque_pop(deque_t *dp) 76 | { 77 | assert(dp); 78 | deque_item_t *i = dp->tail; 79 | assert(i); 80 | void *p = i->p; 81 | 82 | dp->tail = i->prev; 83 | if (i->prev) 84 | i->prev->next = NULL; 85 | else 86 | dp->head = NULL; 87 | free(i); 88 | 89 | return p; 90 | } 91 | 92 | void *deque_peek(deque_t *dp) 93 | { 94 | assert(dp); 95 | deque_item_t *i = dp->tail; 96 | assert(i); 97 | void *p = i->p; 98 | 99 | return p; 100 | } 101 | 102 | void deque_appendleft(deque_t *dp, void *p) 103 | { 104 | assert(dp); 105 | deque_item_t *i = (deque_item_t *)malloc(sizeof(deque_item_t)); 106 | assert(i); 107 | 108 | i->p = p; 109 | i->next = dp->head; 110 | i->prev = NULL; 111 | 112 | if (dp->head) 113 | dp->head->prev = i; 114 | else 115 | dp->tail = i; 116 | dp->head = i; 117 | } 118 | 119 | void *deque_popleft(deque_t *dp) 120 | { 121 | assert(dp); 122 | deque_item_t *i = dp->head; 123 | assert(i); 124 | void *p = i->p; 125 | 126 | dp->head = i->next; 127 | if (i->next) 128 | i->next->prev = NULL; 129 | else 130 | dp->tail = NULL; 131 | free(i); 132 | 133 | return p; 134 | } 135 | 136 | void *deque_peekleft(deque_t *dp) 137 | { 138 | assert(dp); 139 | deque_item_t *i = dp->head; 140 | assert(i); 141 | void *p = i->p; 142 | 143 | return p; 144 | } 145 | 146 | void deque_remove(deque_t *dp, deque_item_t *dip) 147 | { 148 | assert(dp); 149 | assert(dip); 150 | 151 | if (dip->prev) 152 | { 153 | assert(dip->prev->next == dip); 154 | dip->prev->next = dip->next; 155 | } 156 | else 157 | { 158 | assert(dp->head == dip); 159 | dp->head = dip->next; 160 | } 161 | if (dip->next) 162 | { 163 | assert(dip->next->prev == dip); 164 | dip->next->prev = dip->prev; 165 | } 166 | else 167 | { 168 | assert(dp->tail == dip); 169 | dp->tail = dip->prev; 170 | } 171 | 172 | free(dip); 173 | } 174 | 175 | 176 | buffer_t *buffer_new(int cap) 177 | { 178 | assert(cap>0); 179 | buffer_t *bp = (buffer_t *)malloc(sizeof(buffer_t)+cap); 180 | assert(bp); 181 | 182 | buffer_init(bp, cap); 183 | 184 | return bp; 185 | } 186 | 187 | void buffer_init(buffer_t *bp, int cap) 188 | { 189 | bp->cap = cap; 190 | bp->len = 0; 191 | } 192 | 193 | void buffer_free(buffer_t *bp) 194 | { 195 | free(bp); 196 | } 197 | 198 | int buffer_eq(buffer_t *a, buffer_t *b) 199 | { 200 | if (a->len == b->len) 201 | { 202 | const int n = a->len; 203 | for (int i=0; ibuf[i] != b->buf[i]) 205 | return 0; 206 | return 1; 207 | } 208 | return 0; 209 | } 210 | 211 | 212 | static int naive_hash(buffer_t *bp) 213 | { 214 | uint8_t sum = 0; 215 | 216 | for(uint8_t *i=bp->buf; ibuf+bp->len; ++i) 217 | sum ^= *i; 218 | 219 | return sum; 220 | } 221 | 222 | static int ht256_hash(buffer_t *bp) 223 | { 224 | access_t sum = {0}; 225 | uintptr_t p = (uintptr_t)bp->buf; 226 | int n = bp->len; 227 | 228 | if (p&0x01 && n>=1) 229 | { 230 | *sum.u8 ^= *(uint8_t *)p++; 231 | n -= 1; 232 | } 233 | if (p&0x02 && n>=2) 234 | { 235 | *sum.u16 ^= *(uint16_t *)p++; 236 | n -= 2; 237 | } 238 | if (p&0x04 && n>=4) 239 | { 240 | *sum.u32 ^= *(uint32_t *)p++; 241 | n -= 4; 242 | } 243 | while (n>=8) 244 | { 245 | sum.u64 ^= *(uint64_t *)p++; 246 | n -= 8; 247 | } 248 | sum.u32[0] ^= sum.u32[1]; 249 | sum.u32[1] = 0; 250 | if (n>=4) 251 | { 252 | *sum.u32 ^= *(uint32_t *)p++; 253 | n -= 4; 254 | } 255 | sum.u16[0] ^= sum.u16[1]; 256 | sum.u16[1] = 0; 257 | if (n>=2) 258 | { 259 | *sum.u16 ^= *(uint16_t *)p++; 260 | n -= 2; 261 | } 262 | sum.u8[0] ^= sum.u8[1]; 263 | sum.u8[1] = 0; 264 | if (n>=1) 265 | { 266 | *sum.u8 ^= *(uint8_t *)p++; 267 | n -= 1; 268 | } 269 | 270 | 271 | return sum.u8[0]; 272 | } 273 | 274 | static int ht16_hash(buffer_t *bp) 275 | { 276 | uint8_t ret = ht256_hash(bp); 277 | 278 | ret ^= ret >> 4; 279 | 280 | return ret & 0x0F; 281 | } 282 | 283 | hashtable_t *ht256_new(void) 284 | { 285 | hashtable_t *htp = (hashtable_t *)malloc(sizeof(hashtable_t)+256*sizeof(deque_t)); 286 | htp->hash = ht256_hash; 287 | // htp->hash = naive_hash; 288 | htp->nbucket = 256; 289 | 290 | for (int i=0; i<256; ++i) 291 | deque_init(htp->bucket+i); 292 | 293 | return htp; 294 | } 295 | 296 | hashtable_t *ht16_new(void) 297 | { 298 | hashtable_t *htp = (hashtable_t *)malloc(sizeof(hashtable_t)+16*sizeof(deque_t)); 299 | htp->hash = ht16_hash; 300 | htp->nbucket = 16; 301 | 302 | for (int i=0; i<16; ++i) 303 | deque_init(htp->bucket+i); 304 | 305 | return htp; 306 | } 307 | 308 | void ht_deinit(hashtable_t *htp) 309 | { 310 | assert(htp); 311 | 312 | for (int i=0; inbucket; ++i) 313 | // deque_deinit(&htp->bucket[i]); 314 | { 315 | deque_t *dp = &htp->bucket[i]; 316 | while (dp->head) 317 | { 318 | ht_node_t *hnp = (ht_node_t *)deque_popleft(dp); 319 | // buffer_free(hnp->key); 320 | free(hnp); 321 | } 322 | } 323 | } 324 | 325 | void ht_free(hashtable_t *htp) 326 | { 327 | assert(htp); 328 | ht_deinit(htp); 329 | free(htp); 330 | } 331 | 332 | void *ht_search(hashtable_t *htp, buffer_t *key) 333 | { 334 | assert(htp); 335 | assert(key); 336 | 337 | DEQUE_FOREACH(i, htp->bucket+htp->hash(key)) 338 | { 339 | if (buffer_eq(key, ((ht_node_t *)i->p)->key)) 340 | return ((ht_node_t *)i->p)->value; 341 | } 342 | 343 | return NULL; 344 | } 345 | 346 | void *ht_insert(hashtable_t *htp, buffer_t *key, void *value) 347 | { 348 | assert(htp); 349 | assert(key); 350 | 351 | ht_node_t *node = (ht_node_t *)malloc(sizeof(ht_node_t)); 352 | node->key = key; 353 | node->value = value; 354 | 355 | deque_appendleft(&htp->bucket[htp->hash(key)], (void *)node); 356 | } 357 | 358 | int ht_delete(hashtable_t *htp, buffer_t *key) 359 | { 360 | assert(htp); 361 | assert(key); 362 | 363 | deque_t *dp = htp->bucket+htp->hash(key); 364 | DEQUE_FOREACH(i, dp) 365 | { 366 | if (buffer_eq(key, ((ht_node_t *)i->p)->key)) 367 | { 368 | free((ht_node_t *)i->p); 369 | deque_remove(dp, i); 370 | // buffer_free(((ht_node_t *)i)->key); 371 | // free(i); 372 | return 1; 373 | } 374 | } 375 | 376 | return 0; 377 | } 378 | 379 | 380 | void dump_hex(const unsigned char *buf, size_t len, const char *indent) 381 | { 382 | size_t i; 383 | 384 | for(i=0; isa_family) 416 | { 417 | case AF_INET: 418 | memmove(buf, "INET: ", 6); 419 | inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf+6, sizeof(buf)-6); 420 | sprintf(buf+strlen(buf), ":%d", ntohs(((struct sockaddr_in *)sa)->sin_port)); 421 | break; 422 | case AF_INET6: 423 | memmove(buf, "INET6: [", 8); 424 | inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf+8, sizeof(buf)-8); 425 | sprintf(buf+strlen(buf), "]:%d", ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); 426 | break; 427 | default: 428 | memmove(buf, "unknown", 8); 429 | break; 430 | } 431 | 432 | return buf; 433 | } 434 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef UTIL_H 5 | #define UTIL_H 6 | 7 | typedef struct deque_item_s deque_item_t; 8 | 9 | struct deque_item_s { 10 | void *p; 11 | deque_item_t *prev; 12 | deque_item_t *next; 13 | }; 14 | 15 | typedef struct deque_s { 16 | deque_item_t *head; 17 | deque_item_t *tail; 18 | } deque_t; 19 | 20 | #define DEQUE_FOREACH(i, dq) \ 21 | for (deque_item_t *i=(dq)->head; i!=NULL; i=i->next) 22 | 23 | typedef struct buffer_s { 24 | int cap; 25 | int len; 26 | // unsigned char *buf; 27 | unsigned char buf[]; 28 | } buffer_t; 29 | 30 | typedef struct hashtable_s { 31 | int (*hash)(buffer_t *bp); 32 | int nbucket; 33 | deque_t bucket[]; 34 | } hashtable_t; 35 | 36 | typedef struct ht_node_s { 37 | buffer_t *key; 38 | void *value; 39 | } ht_node_t; 40 | 41 | #define HT_FOREACH(htnp, htp) \ 42 | for (int _tmp_index=0; _tmp_index<(htp)->nbucket; ++_tmp_index) \ 43 | for (deque_item_t *_tmp_item=((htp)->bucket[_tmp_index].head); _tmp_item; _tmp_item=_tmp_item->next) \ 44 | for (ht_node_t *(htnp)=(ht_node_t *)_tmp_item->p; htnp; htnp=NULL) 45 | 46 | deque_t *deque_new(void); 47 | void deque_init(deque_t *dp); 48 | void deque_deinit(deque_t *dp); 49 | void deque_free(deque_t *dp); 50 | size_t deque_count(deque_t *dp); 51 | void deque_append(deque_t *dp, void *p); 52 | void *deque_pop(deque_t *dp); 53 | void *deque_peek(deque_t *dp); 54 | void deque_appendleft(deque_t *dp, void *p); 55 | void *deque_popleft(deque_t *dp); 56 | void *deque_peekleft(deque_t *dp); 57 | void deque_remove(deque_t *dp, deque_item_t *dip); 58 | 59 | 60 | buffer_t *buffer_new(int cap); 61 | void buffer_init(buffer_t *bp, int cap); 62 | void buffer_free(buffer_t *bp); 63 | int buffer_eq(buffer_t *a, buffer_t *b); 64 | 65 | hashtable_t *ht256_new(void); 66 | hashtable_t *ht16_new(void); 67 | void ht_deinit(hashtable_t *htp); 68 | void ht_free(hashtable_t *htp); 69 | void *ht_search(hashtable_t *htp, buffer_t *key); 70 | void *ht_insert(hashtable_t *htp, buffer_t *key, void *value); 71 | int ht_delete(hashtable_t *htp, buffer_t *key); 72 | 73 | void dump_hex(const unsigned char *buf, size_t len, const char *indent); 74 | void dump_addr(struct sockaddr *sa, const char *indent); 75 | const char *sdump_addr(struct sockaddr *sa); 76 | 77 | #endif /* UTIL_H */ 78 | --------------------------------------------------------------------------------