├── .gitignore ├── CHANGELOGS ├── Makefile ├── binding.gyp ├── include ├── net.h └── tls.h ├── net.gyp ├── package.json ├── readme.md ├── src ├── net.c └── tls.c └── tests └── simple.c /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | deps/ 3 | test/ 4 | build/ 5 | node_modules/ 6 | 7 | *.DS_Store 8 | *.log 9 | -------------------------------------------------------------------------------- /CHANGELOGS: -------------------------------------------------------------------------------- 1 | 2 | # Change Logs 3 | 4 | 2014-4-13(v0.1.1): 5 | 6 | * add a fake function: tls_get_peer_cert 7 | 8 | * tls_handle_bio_error and tls_handle_ssl_error added for a common usage 9 | 10 | * fix a high cpu bug in uv lines 11 | 12 | * make SSL_MODE_RELEASE_BUFFERS be gracefully imported 13 | 14 | * correct conn_cb and read_cb for ssl 15 | 16 | * fix a memory leak in ssl part -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC ?= gcc 3 | SRC = $(wildcard src/*.c) tests/simple.c 4 | DEPS = $(wildcard deps/*/*.c) 5 | CFLAGS = -std=c99\ 6 | -Iinclude\ 7 | -Isrc\ 8 | -Ideps\ 9 | -Ideps/libuv/include\ 10 | -Ldeps/libuv/build/Release\ 11 | -Wall -Wno-unused-function 12 | LDFLAGS = -lcrypto -lssl -luv -framework CoreFoundation -framework CoreServices 13 | 14 | test: $(SRC) 15 | $(CC) $(CFLAGS) -o $@ $(SRC) $(DEPS) $(LDFLAGS) 16 | 17 | .PHONY: test 18 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "net", 5 | "sources": [ 6 | "src/net.c", 7 | "src/tls.c", 8 | ], 9 | "cflags": [ 10 | "-Wno-trigraphs" 11 | ], 12 | "defines": [ 13 | "HAVE_SNPRINTF" 14 | ], 15 | "include_dirs" : [ 16 | "include", 17 | "deps", 18 | " 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include "tls.h" 11 | 12 | #define NET_OK 0 13 | #define NET_VERSION_MAJOR 0 14 | #define NET_VERSION_MINOR 1 15 | #define NET_VERSION_PATCH 2 16 | #define NET_VERSION_IS_RELEASE 0 17 | 18 | typedef struct net_s net_t; 19 | typedef struct addrinfo net_ai; 20 | typedef struct sockaddr_in socketPair_t; 21 | typedef uv_err_t err_t; 22 | 23 | #define NET_FIELDS \ 24 | NET_CONNECTION_FIELDS \ 25 | NET_UV_FIELDS \ 26 | NET_TLS_FIELDS \ 27 | 28 | 29 | #define NET_CONNECTION_FIELDS \ 30 | char *hostname; \ 31 | int port; \ 32 | int connected; \ 33 | 34 | 35 | #define NET_UV_FIELDS \ 36 | uv_getaddrinfo_t *resolver; \ 37 | uv_loop_t *loop; \ 38 | uv_tcp_t *handle; \ 39 | uv_connect_t *conn; \ 40 | 41 | 42 | #define NET_TLS_FIELDS \ 43 | tls_t *tls; \ 44 | int use_ssl; \ 45 | int tls_established; \ 46 | 47 | 48 | struct net_s { 49 | NET_FIELDS; 50 | void *data; 51 | void (*conn_cb)(net_t*); 52 | void (*read_cb)(net_t*, size_t, char*); 53 | void (*error_cb)(net_t*, err_t, char*); 54 | void (*close_cb)(uv_handle_t*); 55 | }; 56 | 57 | /* 58 | * Create an new network. 59 | */ 60 | net_t * 61 | net_new(char * hostname, int port); 62 | 63 | /* 64 | * Set SSL's Context 65 | */ 66 | int 67 | net_set_tls(net_t * net, tls_ctx * ctx); 68 | 69 | /* 70 | * Do connect to new 71 | */ 72 | int 73 | net_connect(net_t * net); 74 | 75 | /* 76 | * Just close the holding connection 77 | */ 78 | int 79 | net_close(net_t * net, void (*cb)(uv_handle_t*)); 80 | 81 | /* 82 | * free connection 83 | */ 84 | int 85 | net_free(net_t * net); 86 | 87 | /* 88 | * real free function 89 | */ 90 | void 91 | net_free_cb(uv_handle_t * handle); 92 | 93 | /* 94 | * DNS resolve 95 | */ 96 | int 97 | net_resolve(net_t * net); 98 | 99 | /* 100 | * DNS -> IP done, and call `net_resolve_cb` 101 | */ 102 | void 103 | net_resolve_cb(uv_getaddrinfo_t *rv, int stat, net_ai * ai); 104 | 105 | /* 106 | * connect created, and call `net_connect_cb` 107 | */ 108 | void 109 | net_connect_cb(uv_connect_t *conn, int stat); 110 | 111 | /* 112 | * realloc buffer before you read 113 | */ 114 | uv_buf_t 115 | net_alloc(uv_handle_t* handle, size_t size); 116 | 117 | /* 118 | * read buffer 119 | */ 120 | void 121 | net_read(uv_stream_t *handle, ssize_t nread, const uv_buf_t buf); 122 | 123 | /* 124 | * write buffer 125 | */ 126 | int 127 | net_write(net_t * net, char * buf); 128 | 129 | /* 130 | * write buffer with length specified 131 | */ 132 | int 133 | net_write2(net_t * net, char * buf, unsigned int len); 134 | 135 | /* 136 | * return use_ssl 137 | */ 138 | int 139 | net_use_ssl(net_t * net); 140 | 141 | /* 142 | * continue to read after on data 143 | */ 144 | int 145 | net_resume(net_t * net); 146 | 147 | /* 148 | * pause read 149 | */ 150 | int 151 | net_pause(net_t * net); 152 | 153 | /* 154 | * set error_cb 155 | */ 156 | int 157 | net_set_error_cb(net_t * net, void * cb); 158 | 159 | /* 160 | * write buffer, and call `net_write_cb`. 161 | */ 162 | void 163 | net_write_cb(uv_write_t *writer, int stat); -------------------------------------------------------------------------------- /include/tls.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2014 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define NOT_SSL 0x00 15 | #define USE_SSL 0x01 16 | #define SSL_CHUNK_SIZE 512 17 | 18 | typedef SSL_CTX tls_ctx; 19 | typedef struct tls_s { 20 | SSL_CTX * ctx; 21 | SSL * ssl; 22 | BIO * bio_in; 23 | BIO * bio_out; 24 | buffer_t * buffer; 25 | int connected; 26 | char * data; 27 | char buf[SSL_CHUNK_SIZE]; /* internal usage */ 28 | } tls_t; 29 | 30 | static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL 31 | | ASN1_STRFLGS_ESC_MSB 32 | | XN_FLAG_SEP_MULTILINE 33 | | XN_FLAG_FN_SN; 34 | 35 | /* 36 | * initialize the ssl 37 | */ 38 | void 39 | ssl_init(); 40 | 41 | /* 42 | * destroy the ssl settings and internal tables 43 | */ 44 | void 45 | ssl_destroy(); 46 | 47 | /* 48 | * create a context for ssl 49 | */ 50 | tls_ctx * 51 | tls_ctx_new(); 52 | 53 | /* 54 | * create a tls instance 55 | */ 56 | tls_t * 57 | tls_create(tls_ctx * ctx); 58 | 59 | /* 60 | * shutdown tls 61 | */ 62 | int 63 | tls_shutdown(tls_t * tls); 64 | 65 | /* 66 | * destroy a tls instance 67 | */ 68 | int 69 | tls_free(tls_t * tls); 70 | 71 | /* 72 | * get peer certification info 73 | */ 74 | int 75 | tls_get_peer_cert(tls_t * tls); 76 | 77 | /* 78 | * do connect to tls 79 | */ 80 | int 81 | tls_connect(tls_t * tls); 82 | 83 | /* 84 | * handle error in bio 85 | */ 86 | int 87 | tls_handle_bio_error(tls_t * tls, int err); 88 | 89 | /* 90 | * handle error in ssl 91 | */ 92 | int 93 | tls_handle_ssl_error(tls_t *tls, int err); 94 | 95 | /* 96 | * a port in tls for `bio_read` 97 | */ 98 | int 99 | tls_bio_read(tls_t * tls, int len); 100 | 101 | /* 102 | * a port in tls for `bio_write` 103 | */ 104 | int 105 | tls_bio_write(tls_t * tls, char * written, int len); 106 | 107 | /* 108 | * read 109 | */ 110 | int 111 | tls_read(tls_t * tls); 112 | 113 | /* 114 | * write 115 | */ 116 | int 117 | tls_write(tls_t * tls, char * written, int len); 118 | 119 | /* 120 | * write a tls packet 121 | */ 122 | #define REQUEST_TLS_WRITE(name, cmd, read, req) do { \ 123 | tls_write(req->tls, cmd); \ 124 | do { \ 125 | read = tls_bio_read(req->tls, 0); \ 126 | if (read > 0) { \ 127 | REQUEST_WRITE(req, req->tls->buf, read, name); \ 128 | } \ 129 | } while (read > 0); \ 130 | } \ 131 | while (0) 132 | -------------------------------------------------------------------------------- /net.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'net', 5 | 'type': '<(library)', 6 | 'sources': [ 7 | 'src/net.c', 8 | 'src/tls.c', 9 | ], 10 | 'include_dirs': [ 11 | '.', 12 | 'include', 13 | 'deps', 14 | 'deps/buffer', 15 | 'deps/libuv', 16 | ], 17 | 'dependencies': [ 18 | 'deps/libuv/uv.gyp:libuv', 19 | ], 20 | 'cflags': [ 21 | '-std=c99', 22 | '-Wall', 23 | ], 24 | 'conditions': [ 25 | ['OS=="mac"', { 26 | 'xcode_settings': { 27 | 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 28 | 'GCC_C_LANGUAGE_STANDARD': 'c99' 29 | } 30 | }] 31 | ] 32 | }, 33 | 34 | { 35 | 'target_name': 'run-tests', 36 | 'type': 'executable', 37 | 'sources': [ 38 | 'tests/simple.c', 39 | ], 40 | 'include_dirs': [ 41 | '.', 42 | 'include', 43 | 'deps', 44 | 'deps/buffer', 45 | 'deps/libuv', 46 | ], 47 | 'dependencies': [ 48 | 'net', 49 | ], 50 | 'cflags': [ 51 | '-std=c99', 52 | '-Wall', 53 | ], 54 | 'conditions': [ 55 | ['OS=="mac"', { 56 | 'xcode_settings': { 57 | 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 58 | 'GCC_C_LANGUAGE_STANDARD': 'c99' 59 | } 60 | }] 61 | ] 62 | }, 63 | 64 | ] 65 | } 66 | 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "net", 3 | "version": "0.1.1", 4 | "repo": "clibs/net", 5 | "description": "network library for c", 6 | "keywords": [ 7 | "network", 8 | "openssl", 9 | "libuv" 10 | ], 11 | "license": "MIT", 12 | "src": [ 13 | "src/net.c", 14 | "src/tls.c", 15 | "include/net.h", 16 | "include/tls.h" 17 | ], 18 | "dependencies": { 19 | "buffer": "*", 20 | "nan": "^1.3.0", 21 | "uv": "*" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # net-client 3 | 4 | Simple network client 5 | 6 | ### Requirement 7 | 8 | * libuv 0.10.x 9 | 10 | * buffer 0.2.0 11 | 12 | ### Installation 13 | 14 | ```sh 15 | $ clib install clibs/net 16 | $ git clone https://github.com/joyent/libuv.git deps/libuv 17 | $ checkout v0.10.25 18 | ``` 19 | 20 | ### Run tests 21 | 22 | ```sh 23 | make test 24 | ./test 25 | ``` 26 | 27 | ### Example 28 | 29 | ```c 30 | static void 31 | imap_parser(net_t * net, size_t read, char * buf) { 32 | printf("%s\n", buf); 33 | printf("%zu\n", read); 34 | } 35 | 36 | int 37 | main(int argc, char *argv[]) { 38 | ssl_init(); 39 | tls_ctx * ctx = tls_ctx_new(); 40 | net_t * net = net_new("imap.gmail.com", 993); 41 | net->read_cb = imap_parser; 42 | 43 | // convert this socket to ssl 44 | net_set_tls(net, ctx); 45 | net_connect(net); 46 | 47 | uv_run(net->loop, UV_RUN_DEFAULT); 48 | } 49 | ``` 50 | 51 | ### License 52 | 53 | MIT 54 | -------------------------------------------------------------------------------- /src/net.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "net.h" 5 | 6 | net_t * 7 | net_new(char * hostname, int port) { 8 | net_t * net = (net_t*) malloc(sizeof(net_t)); 9 | net->loop = uv_default_loop(); 10 | net->hostname = hostname; 11 | net->port = port; 12 | net->connected = 0; 13 | net->tls_established = 0; 14 | net->use_ssl = 0; 15 | net->conn_cb = NULL; 16 | net->read_cb = NULL; 17 | net->error_cb = NULL; 18 | net->close_cb = NULL; 19 | net->handle = (uv_tcp_t *) malloc(sizeof(uv_tcp_t)); 20 | net->conn = (uv_connect_t *) malloc(sizeof(uv_connect_t)); 21 | net->handle->data 22 | = net->conn->data 23 | = (void *) net; 24 | 25 | return net; 26 | } 27 | 28 | int 29 | net_set_tls(net_t * net, tls_ctx * ctx) { 30 | net->use_ssl = USE_SSL; 31 | net->tls = tls_create(ctx); 32 | return NET_OK; 33 | } 34 | 35 | int 36 | net_connect(net_t * net) { 37 | net_resolve(net); 38 | return NET_OK; 39 | } 40 | 41 | int 42 | net_close(net_t * net, void (*cb)(uv_handle_t*)) { 43 | int r = net->connected; 44 | if (r == 1) { 45 | net->connected = 0; 46 | net->tls_established = 0; 47 | if (net->use_ssl) { 48 | tls_shutdown(net->tls); 49 | } 50 | uv_close((uv_handle_t*)net->handle, cb); 51 | if (net->use_ssl) { 52 | tls_free(net->tls); 53 | } 54 | } 55 | 56 | fprintf(stdout, "> close socket for %s\n", net->hostname); 57 | return r; 58 | } 59 | 60 | int 61 | net_free(net_t * net) { 62 | net_close(net, NULL); 63 | free(net->resolver); 64 | free(net); 65 | return NET_OK; 66 | } 67 | 68 | void 69 | net_free_cb(uv_handle_t * handle) { 70 | net_t * net = (net_t *) handle->data; 71 | if (net->handle != NULL) { 72 | free(net->handle); 73 | net->handle = NULL; 74 | } 75 | if (net->conn != NULL) { 76 | free(net->conn); 77 | net->conn = NULL; 78 | } 79 | if (net->resolver != NULL) { 80 | free(net->resolver); 81 | net->resolver = NULL; 82 | } 83 | if (net != NULL) { 84 | free(net); 85 | net = NULL; 86 | } 87 | } 88 | 89 | int 90 | net_resolve(net_t * net) { 91 | net_ai hints; 92 | int ret; 93 | char buf[6]; 94 | 95 | snprintf(buf, sizeof(buf), "%d", net->port); 96 | memset(&hints, 0, sizeof(struct addrinfo)); 97 | hints.ai_family = AF_INET; 98 | hints.ai_socktype = SOCK_STREAM; 99 | 100 | net->resolver = malloc(sizeof(uv_getaddrinfo_t)); 101 | if (!net->resolver) { 102 | /* 103 | * TODO(Yorkie): depent parital handles 104 | */ 105 | return -1; 106 | } 107 | 108 | net->resolver->data = (void *) net; 109 | ret = uv_getaddrinfo(net->loop, net->resolver, 110 | net_resolve_cb, net->hostname, NULL, &hints); 111 | 112 | return ret; 113 | } 114 | 115 | void 116 | net_resolve_cb(uv_getaddrinfo_t *rv, int stat, net_ai * ai) { 117 | net_t * net = (net_t*) rv->data; 118 | err_t err; 119 | socketPair_t dest; 120 | char addr[INET6_ADDRSTRLEN]; 121 | int ret; 122 | 123 | if (stat != 0) { 124 | err = uv_last_error(net->loop); 125 | if (net->error_cb) { 126 | net->error_cb(net, err, (char *) uv_strerror(err)); 127 | } else { 128 | printf("error(%s:%d) %s", net->hostname, net->port, (char *) uv_strerror(err)); 129 | net_free(net); 130 | } 131 | return; 132 | } 133 | 134 | uv_ip4_name((socketPair_t *) ai->ai_addr, addr, INET6_ADDRSTRLEN); 135 | dest = uv_ip4_addr(addr, net->port); 136 | 137 | /* 138 | * create tcp instance. 139 | */ 140 | uv_tcp_init(net->loop, net->handle); 141 | ret = uv_tcp_connect(net->conn, net->handle, dest, net_connect_cb); 142 | if (ret != NET_OK) { 143 | err = uv_last_error(net->loop); 144 | if (net->error_cb) { 145 | net->error_cb(net, err, (char *) uv_strerror(err)); 146 | } else { 147 | printf("error(%s:%d) %s", net->hostname, net->port, (char *) uv_strerror(err)); 148 | net_free(net); 149 | } 150 | return; 151 | } 152 | 153 | /* 154 | * free 155 | */ 156 | uv_freeaddrinfo(ai); 157 | } 158 | 159 | void 160 | net_connect_cb(uv_connect_t *conn, int stat) { 161 | net_t * net = (net_t *) conn->data; 162 | err_t err; 163 | int read; 164 | 165 | if (stat < 0) { 166 | err = uv_last_error(net->loop); 167 | if (net->error_cb) { 168 | net->error_cb(net, err, (char *) uv_strerror(err)); 169 | } else { 170 | printf("error(%s:%d) %s", net->hostname, net->port, (char *) uv_strerror(err)); 171 | net_free(net); 172 | } 173 | return; 174 | } 175 | 176 | /* 177 | * change the `connected` state 178 | */ 179 | net->connected = 1; 180 | 181 | /* 182 | * read buffers via uv 183 | */ 184 | uv_read_start((uv_stream_t *) net->handle, net_alloc, net_read); 185 | 186 | /* 187 | * call `conn_cb`, the tcp connection has been 188 | * established in user-land. 189 | */ 190 | if (net->use_ssl == NOT_SSL && net->conn_cb != NULL) { 191 | net->conn_cb(net); 192 | } 193 | 194 | /* 195 | * Handle TLS Partial 196 | */ 197 | if (net->use_ssl == USE_SSL && tls_connect(net->tls) == NET_OK) { 198 | read = 0; 199 | do { 200 | read = tls_bio_read(net->tls, 0); 201 | if (read > 0) { 202 | char buf[read]; 203 | uv_write_t * req = malloc(sizeof(uv_write_t)); 204 | req->data = net; 205 | memset(buf, 0, read); 206 | memcpy(buf, net->tls->buf, read); 207 | uv_buf_t uvbuf = uv_buf_init(buf, read); 208 | uv_write(req, (uv_stream_t*)net->handle, &uvbuf, 1, net_write_cb); 209 | } 210 | } while (read > 0); 211 | } 212 | } 213 | 214 | uv_buf_t 215 | net_alloc(uv_handle_t* handle, size_t size) { 216 | char * base = (char *) calloc(size, 1); 217 | return uv_buf_init(base, size); 218 | } 219 | 220 | void 221 | net_read(uv_stream_t *handle, ssize_t nread, const uv_buf_t buf) { 222 | 223 | net_t * net = (net_t *) handle->data; 224 | err_t err; 225 | 226 | if (nread < 0) { 227 | err = uv_last_error(net->loop); 228 | if (net->error_cb) { 229 | net->error_cb(net, err, (char *) uv_strerror(err)); 230 | } else { 231 | printf("error(%s:%d) %s", net->hostname, net->port, (char *) uv_strerror(err)); 232 | net_free(net); 233 | } 234 | return; 235 | } 236 | 237 | /* 238 | * BIO Return rule: 239 | * All these functions return either the amount of data successfully 240 | * read or written (if the return value is positive) or that no data 241 | * was successfully read or written if the result is 0 or -1. If the 242 | * return value is -2 then the operation is not implemented in the specific BIO type. 243 | */ 244 | if (net->use_ssl) { 245 | tls_bio_write(net->tls, buf.base, nread); 246 | free(buf.base); 247 | 248 | int read = 0; 249 | int stat = tls_read(net->tls); 250 | if (stat == 1) { 251 | /* 252 | * continue: Say hello 253 | */ 254 | do { 255 | read = tls_bio_read(net->tls, 0); 256 | if (read > 0) { 257 | char buf2[read]; 258 | uv_write_t * req = malloc(sizeof(uv_write_t)); 259 | req->data = net; 260 | memset(buf2, 0, read); 261 | memcpy(buf2, net->tls->buf, read); 262 | uv_buf_t uvbuf = uv_buf_init(buf2, read); 263 | uv_write(req, (uv_stream_t*)net->handle, &uvbuf, 1, net_write_cb); 264 | } 265 | } while (read > 0); 266 | 267 | } else { 268 | /* 269 | * SSL Connection is created 270 | * Here need to call user-land callback 271 | */ 272 | if (!net->tls_established) { 273 | net->tls_established = 1; 274 | if (net->conn_cb != NULL) { 275 | net->conn_cb(net); 276 | } 277 | } 278 | 279 | /* 280 | * read buffer 281 | */ 282 | if (stat == 0) { 283 | if (buffer_string(net->tls->buffer) > 0) 284 | // uv_read_stop((uv_stream_t*)net->handle); 285 | 286 | if (net->read_cb != NULL && net->connected && net->tls_established) { 287 | net->read_cb(net, buffer_length(net->tls->buffer), 288 | buffer_string(net->tls->buffer)); 289 | } 290 | } 291 | } 292 | return; 293 | } 294 | 295 | /* 296 | * TCP Part, no SSL, just proxy of uv. 297 | */ 298 | uv_read_stop(handle); 299 | buf.base[nread] = 0; 300 | if (net->read_cb != NULL) { 301 | net->read_cb(net, nread, buf.base); 302 | free(buf.base); 303 | } 304 | } 305 | 306 | int 307 | net_write(net_t * net, char * buf) { 308 | return net_write2(net, buf, strlen(buf)); 309 | } 310 | 311 | int 312 | net_write2(net_t * net, char * buf, unsigned int len) { 313 | uv_write_t * req; 314 | uv_buf_t uvbuf; 315 | int read = 0; 316 | 317 | switch (net->use_ssl) { 318 | case USE_SSL: 319 | tls_write(net->tls, buf, (int)len); 320 | do { 321 | read = tls_bio_read(net->tls, 0); 322 | if (read > 0) { 323 | req = (uv_write_t *) malloc(sizeof(uv_write_t)); 324 | req->data = net; 325 | uvbuf = uv_buf_init(net->tls->buf, read); 326 | uv_write(req, (uv_stream_t*)net->handle, 327 | &uvbuf, 328 | 1, 329 | net_write_cb); 330 | } 331 | } while (read > 0); 332 | break; 333 | 334 | case NOT_SSL: 335 | req = (uv_write_t *) malloc(sizeof(uv_write_t)); 336 | req->data = net; 337 | uvbuf = uv_buf_init(buf, len); 338 | uv_write(req, (uv_stream_t*)net->handle, 339 | &uvbuf, 340 | 1, 341 | net_write_cb); 342 | break; 343 | } 344 | 345 | return NET_OK; 346 | } 347 | 348 | int 349 | net_use_ssl(net_t * net) { 350 | return net->use_ssl == USE_SSL; 351 | } 352 | 353 | int 354 | net_resume(net_t * net) { 355 | uv_read_start((uv_stream_t *)net->handle, net_alloc, net_read); 356 | return NET_OK; 357 | } 358 | 359 | int 360 | net_pause(net_t * net) { 361 | uv_read_stop((uv_stream_t *)net->handle); 362 | return NET_OK; 363 | } 364 | 365 | int 366 | net_set_error_cb(net_t * net, void * cb) { 367 | net->error_cb = cb; 368 | return NET_OK; 369 | } 370 | 371 | void 372 | net_write_cb(uv_write_t *req, int stat) { 373 | free(req); 374 | net_resume((net_t*)req->data); 375 | } 376 | -------------------------------------------------------------------------------- /src/tls.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2014 4 | */ 5 | 6 | #include 7 | #include "tls.h" 8 | 9 | void 10 | ssl_init() { 11 | SSL_library_init(); 12 | OpenSSL_add_all_algorithms(); 13 | SSL_load_error_strings(); 14 | ERR_load_crypto_strings(); 15 | } 16 | 17 | void 18 | ssl_destroy() { 19 | EVP_cleanup(); 20 | ERR_free_strings(); 21 | } 22 | 23 | tls_ctx * 24 | tls_ctx_new() { 25 | tls_ctx *ctx = SSL_CTX_new(SSLv23_method()); 26 | #ifdef SSL_OP_NO_COMPRESSION 27 | SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); 28 | #endif 29 | 30 | return ctx; 31 | } 32 | 33 | tls_t * 34 | tls_create(tls_ctx *ctx) { 35 | tls_t * tls = (tls_t *) malloc(sizeof(tls_t)+1); 36 | if (tls == NULL) 37 | fprintf(stderr, "tls> %s", "Out of Memory"); 38 | 39 | tls->ctx = ctx; 40 | tls->ssl = SSL_new(tls->ctx); 41 | tls->bio_in = BIO_new(BIO_s_mem()); 42 | tls->bio_out = BIO_new(BIO_s_mem()); 43 | tls->connected = -1; 44 | tls->buffer = buffer_new(); 45 | 46 | #ifdef SSL_MODE_RELEASE_BUFFERS 47 | long mode = SSL_get_mode(tls->ssl); 48 | SSL_set_mode(tls->ssl, mode | SSL_MODE_RELEASE_BUFFERS); 49 | #endif 50 | 51 | if (tls->ssl == NULL) 52 | printf("tls> %s", "Out of Memory"); 53 | 54 | SSL_set_connect_state(tls->ssl); 55 | SSL_set_bio(tls->ssl, tls->bio_in, tls->bio_out); 56 | return tls; 57 | } 58 | 59 | int 60 | tls_shutdown(tls_t * tls) { 61 | assert(tls != NULL); 62 | if (SSL_shutdown(tls->ssl) == 0) { 63 | SSL_shutdown(tls->ssl); 64 | } 65 | return 0; 66 | } 67 | 68 | int 69 | tls_free(tls_t * tls) { 70 | if (tls->ssl) { 71 | SSL_free(tls->ssl); 72 | SSL_CTX_free(tls->ctx); 73 | tls->ssl = NULL; 74 | } 75 | 76 | buffer_free(tls->buffer); 77 | free(tls); 78 | return 0; 79 | } 80 | 81 | int 82 | tls_get_peer_cert(tls_t *tls) { 83 | X509* peer_cert = SSL_get_peer_certificate(tls->ssl); 84 | if (peer_cert != NULL) { 85 | /* 86 | * TODO(Yorkie): This function is used just for debug 87 | */ 88 | X509_free(peer_cert); 89 | } 90 | return 0; 91 | } 92 | 93 | int 94 | tls_connect(tls_t *tls) { 95 | int rv; 96 | int er; 97 | 98 | rv = SSL_do_handshake(tls->ssl); 99 | if (rv == 1) { 100 | /* 101 | * `SSL_do_handshake()` could not return 1, 102 | * that caused no message could be returned in `SSL_get_error()`. 103 | * TODO(Yorkie): handle error, exit? 104 | */ 105 | return -1; 106 | } 107 | 108 | if (!SSL_is_init_finished(tls->ssl)) 109 | er = SSL_connect(tls->ssl); 110 | else 111 | return -1; 112 | 113 | if (er < 0 && SSL_get_error(tls->ssl, er) == SSL_ERROR_WANT_READ) 114 | return 0; 115 | else 116 | return -1; 117 | } 118 | 119 | int 120 | tls_handle_bio_error(tls_t *tls, int err) { 121 | int rv; 122 | int retry = BIO_should_retry(tls->bio_out); 123 | if (BIO_should_write(tls->bio_out)) 124 | rv = -retry; 125 | else if (BIO_should_read(tls->bio_out)) 126 | rv = -retry; 127 | else { 128 | char ssl_error_buf[512]; 129 | ERR_error_string_n(err, ssl_error_buf, sizeof(ssl_error_buf)); 130 | fprintf(stderr, "[%p] BIO: read failed: (%d) %s\n", tls->ssl, err, ssl_error_buf); 131 | return err; 132 | } 133 | return err; 134 | } 135 | 136 | int 137 | tls_handle_ssl_error(tls_t *tls, int err) { 138 | int ret; 139 | int rv = SSL_get_error(tls->ssl, err); 140 | switch (rv) { 141 | case SSL_ERROR_WANT_READ: 142 | ret = 1; 143 | break; 144 | default: 145 | ret = -2; 146 | break; 147 | } 148 | return ret; 149 | } 150 | 151 | int 152 | tls_bio_read(tls_t *tls, int buf_len) { 153 | if (buf_len == 0) { 154 | buf_len = sizeof(tls->buf); 155 | } 156 | memset(tls->buf, 0, buf_len); 157 | 158 | int ret = BIO_read(tls->bio_out, tls->buf, buf_len); 159 | if (ret >= 0) { 160 | tls->buf[ret] = 0; 161 | return ret; 162 | } else { 163 | return tls_handle_bio_error(tls, ret); 164 | } 165 | } 166 | 167 | int 168 | tls_bio_write(tls_t *tls, char *buf, int len) { 169 | int ret = BIO_write(tls->bio_in, buf, len); 170 | if (ret >= 0) 171 | return ret; 172 | else 173 | return tls_handle_bio_error(tls, ret); 174 | } 175 | 176 | int 177 | tls_read(tls_t *tls) { 178 | int err; 179 | int ret; 180 | int read; 181 | 182 | int done = SSL_is_init_finished(tls->ssl); 183 | if (!done) { 184 | err = SSL_connect(tls->ssl); 185 | if (err <= 0) { 186 | return tls_handle_ssl_error(tls, err); 187 | } 188 | 189 | /* 190 | * TODO(Yorkie): returns not 1 nor < 0 191 | */ 192 | assert(err == 1); 193 | } 194 | 195 | /* finished */ 196 | buffer_clear(tls->buffer); 197 | 198 | ret = -1; 199 | do { 200 | read = SSL_read(tls->ssl, tls->buf, SSL_CHUNK_SIZE); 201 | if (read > 0) { 202 | ret = 0; 203 | tls->buf[read] = 0; 204 | buffer_append(tls->buffer, tls->buf); 205 | } else { 206 | tls_handle_ssl_error(tls, read); 207 | } 208 | } while (read > 0); 209 | 210 | if (tls->connected == -1) { 211 | tls->connected = 1; 212 | } else { 213 | ret = 0; 214 | } 215 | return ret; 216 | } 217 | 218 | int 219 | tls_write(tls_t *tls, char *buf_w, int len) { 220 | return SSL_write(tls->ssl, buf_w, len); 221 | } -------------------------------------------------------------------------------- /tests/simple.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * TODO(Yorkie): Expected to be add a graceful test 4 | * 5 | */ 6 | 7 | #include 8 | #include 9 | #include "net.h" 10 | #include "tls.h" 11 | #include "uv.h" 12 | 13 | static void 14 | read_cb(net_t * net, size_t read, char * buf) { 15 | printf("%s\n", buf); 16 | printf("%zu\n", read); 17 | assert(read > 0); 18 | } 19 | 20 | static void 21 | test_socket() { 22 | ssl_init(); 23 | tls_ctx * ctx = tls_ctx_new(); 24 | net_t * net = net_new("imap.gmail.com", 993); 25 | net->read_cb = read_cb; 26 | net_set_tls(net, ctx); 27 | net_connect(net); 28 | } 29 | 30 | static void 31 | test_tls() { 32 | net_t * net = net_new("imap.qq.com", 143); 33 | net->read_cb = read_cb; 34 | net_connect(net); 35 | } 36 | 37 | int 38 | main(int argc, char ** argv) { 39 | test_socket(); 40 | test_tls(); 41 | uv_run(net->loop, UV_RUN_DEFAULT); 42 | return 0; 43 | } --------------------------------------------------------------------------------