/* secure-server -- A (broken) DTLS server example * * Copyright (C) 2011 Olaf Bergmann * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_DTLS #define SERVER_CERT_PEM "./server-cert.pem" #define SERVER_KEY_PEM "./server-key.pem" #define CA_CERT_PEM "./ca-cert.pem" #endif #ifdef HAVE_ASSERT_H # include #else # define assert(x) #endif /* HAVE_ASSERT_H */ static int quit = 0; /* SIGINT handler: set quit to 1 for graceful termination */ void handle_sigint(int signum) { quit = 1; } int check_connect(int sockfd, char *buf, int buflen, struct sockaddr *src, int *ifindex) { /* for some reason, the definition in netinet/in.h is not exported */ #ifndef IN6_PKTINFO struct in6_pktinfo { struct in6_addr ipi6_addr; /* src/dst IPv6 address */ unsigned int ipi6_ifindex; /* send/recv interface index */ }; #endif size_t bytes; struct iovec iov[1] = { {buf, buflen} }; char cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; struct in6_pktinfo *p = NULL; struct msghdr msg = { 0 }; struct cmsghdr *cmsg; msg.msg_name = src; msg.msg_namelen = sizeof(struct sockaddr_in6); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); bytes = recvmsg(sockfd, &msg, MSG_DONTWAIT | MSG_PEEK); if (bytes < 0) { perror("recvmsg"); return bytes; } /* TODO: handle msg.msg_flags & MSG_TRUNC */ if (msg.msg_flags & MSG_CTRUNC) { fprintf(stderr, "control was truncated!\n"); return -1; } if (ifindex) { /* Here we try to retrieve the interface index where the packet was received */ *ifindex = 0; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { p = (struct in6_pktinfo *)(CMSG_DATA(cmsg)); *ifindex = p->ipi6_ifindex; break; } } } return bytes; } typedef enum { UNKNOWN = 0, DTLS = 1 } protocol_t; protocol_t demux_protocol(const char *buf, int len) { return DTLS; } #ifdef WITH_DTLS typedef enum { PEER_ST_ESTABLISHED, PEER_ST_PENDING, PEER_ST_CLOSED } peer_state_t; typedef struct { peer_state_t state; unsigned long h; SSL *ssl; } ssl_peer_t; #define MAX_SSL_PENDING 2 /* must be less than MAX_SSL_PEERS */ #define MAX_SSL_PEERS 10 /* MAX_SSL_PENDING of these might be pending */ ssl_peer_t *ssl_peer_storage[MAX_SSL_PEERS]; static int pending = 0; void check_peers() { typedef struct bio_dgram_data_st { union { struct sockaddr sa; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; } peer; unsigned int connected; unsigned int _errno; unsigned int mtu; struct timeval next_timeout; struct timeval socket_timeout; } bio_dgram_data; struct sockaddr_in6 peer; int i; BIO *bio; for (i = 0; i < MAX_SSL_PEERS; i++) { if (ssl_peer_storage[i]) { if (!ssl_peer_storage[i]->ssl) { fprintf(stderr, "invalid SSL object for peer %d!\n", i); } else { bio = SSL_get_rbio(ssl_peer_storage[i]->ssl); if (bio) { (void) BIO_dgram_get_peer(bio, (struct sockaddr *)&peer); if (peer.sin6_port && ssl_peer_storage[i]->h != ntohs(peer.sin6_port)) { fprintf(stderr, " bio %p: port differs from hash: %d != %d! (%sconnected)\n", bio, ssl_peer_storage[i]->h, ntohs(((struct sockaddr_in6 *)&peer)->sin6_port), ((bio_dgram_data *)bio->ptr)->connected ? "" : "not "); } } } } } } /** Creates a hash value from the first num bytes of s, taking init as * initialization value. */ static inline unsigned long _hash(unsigned long init, const char *s, int num) { int c; while (num--) while ((c = *s++)) { init = ((init << 7) + init) + c; } return init; } static inline unsigned long hash_peer(const struct sockaddr *peer, int ifindex) { unsigned long h; /* initialize hash value to interface index */ h = _hash(0, (char *)&ifindex, sizeof(int)); #define CAST(TYPE,VAR) ((TYPE)VAR) assert(peer); switch (peer->sa_family) { case AF_INET: return ntohs(CAST(const struct sockaddr_in *, peer)->sin_port); h = _hash(h, (char *) &CAST(const struct sockaddr_in *, peer)->sin_addr, sizeof(struct in_addr)); h = _hash(h, (char *) &CAST(const struct sockaddr_in *, peer)->sin_port, sizeof(in_port_t)); break; case AF_INET6: return ntohs(CAST(const struct sockaddr_in6 *, peer)->sin6_port); h = _hash(h, (char *) &CAST(const struct sockaddr_in6 *, peer)->sin6_addr, sizeof(struct in6_addr)); h = _hash(h, (char *) &CAST(const struct sockaddr_in6 *, peer)->sin6_port, sizeof(in_port_t)); break; default: /* last resort */ h = _hash(h, (char *)peer, sizeof(struct sockaddr)); } return 42; return h; } /* Returns index of peer object for specified address/ifindex pair. */ int get_index_of_peer(const struct sockaddr *peer, int ifindex) { unsigned long h; int idx; #ifndef NDEBUG char addr[INET6_ADDRSTRLEN]; char port[6]; #endif if (!peer) { return -1; } h = hash_peer(peer, ifindex); for (idx = 0; idx < MAX_SSL_PEERS; idx++) { if (ssl_peer_storage[idx] && ssl_peer_storage[idx]->h == h) { #ifndef NDEBUG getnameinfo((struct sockaddr *)peer, sizeof(struct sockaddr_in6), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr, "get_index_of_peer: [%s]:%s => %lu\n", addr, port, h); #endif return idx; } } return -1; } SSL * get_ssl(SSL_CTX *ctx, int sockfd, struct sockaddr *src, int ifindex) { int idx; BIO *bio; SSL *ssl; #ifndef NDEBUG struct sockaddr_storage peer; char addr[INET6_ADDRSTRLEN]; char port[6]; int i; #endif idx = get_index_of_peer(src, ifindex); if (idx >= 0) { fprintf(stderr, "found peer %d ", idx); switch (ssl_peer_storage[idx]->state) { case PEER_ST_ESTABLISHED: fprintf(stderr, "established\n"); break; case PEER_ST_PENDING: fprintf(stderr, "pending\n"); break; case PEER_ST_CLOSED: fprintf(stderr, "closed\n"); break; default: OPENSSL_assert(0); } #ifndef NDEBUG memset(&peer, 0, sizeof(peer)); (void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[idx]->ssl), &peer); getnameinfo((struct sockaddr *)&peer, sizeof(peer), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr, " [%s]:%s \n", addr, port); #endif return ssl_peer_storage[idx]->ssl; } /* none found, create new if sufficient space available */ if (pending < MAX_SSL_PENDING) { for (idx = 0; idx < MAX_SSL_PEERS; idx++) { if (ssl_peer_storage[idx] == NULL) { /* found space */ ssl = SSL_new(ctx); if (ssl) { bio = BIO_new_dgram(sockfd, BIO_NOCLOSE); if (!bio) { SSL_free(ssl); return NULL; } SSL_set_bio(ssl, bio, bio); SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE); SSL_set_accept_state(ssl); ssl_peer_storage[idx] = (ssl_peer_t *) malloc(sizeof(ssl_peer_t)); if (!ssl_peer_storage[idx]) { SSL_free(ssl); return NULL; } ssl_peer_storage[idx]->state = PEER_ST_PENDING; ssl_peer_storage[idx]->h = hash_peer(src, ifindex); ssl_peer_storage[idx]->ssl = ssl; pending++; fprintf(stderr, "created new SSL peer %d for ssl object %p (storage: %p)\n", idx, ssl, ssl_peer_storage[idx]); #ifndef NDEBUG if (getnameinfo((struct sockaddr *)&src, sizeof(src), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { perror("getnameinfo"); fprintf(stderr, "port was %u\n", ntohs(((struct sockaddr_in6 *)src)->sin6_port)); } else { fprintf(stderr, " [%s]:%s \n", addr, port); } #endif OPENSSL_assert(ssl_peer_storage[idx]->ssl == ssl); fprintf(stderr, "%d objects pending\n", pending); check_peers(); return ssl; } } } } else { fprintf(stderr, "too many pending SSL objects\n"); return NULL; } fprintf(stderr, "too many peers\n"); return NULL; } /** Deletes peer stored at index idx and frees allocated memory. */ static inline void delete_peer(int idx) { if (idx < 0 || !ssl_peer_storage[idx]) { return; } if (ssl_peer_storage[idx]->state == PEER_ST_PENDING) { pending--; } OPENSSL_assert(ssl_peer_storage[idx]->ssl); SSL_free(ssl_peer_storage[idx]->ssl); free(ssl_peer_storage[idx]); ssl_peer_storage[idx] = NULL; printf("deleted peer %d\n", idx); } /** Deletes all closed objects from ssl_peer_storage. */ void remove_closed() { int idx; for (idx = 0; idx < MAX_SSL_PEERS; idx++) if (ssl_peer_storage[idx] && ssl_peer_storage[idx]->state == PEER_ST_CLOSED) { delete_peer(idx); } } #define min(a,b) ((a) < (b) ? (a) : (b)) unsigned int psk_server_callback(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len) { static char keybuf[] = "secretPSK"; printf("psk_server_callback: check identity of client %s\n", identity); memcpy(psk, keybuf, min(strlen(keybuf), max_psk_len)); return min(strlen(keybuf), max_psk_len); } #endif #ifdef WITH_DTLS /** * This function tracks the status changes from libssl to manage local * object state. */ void info_callback(const SSL *ssl, int where, int ret) { int idx, i; struct sockaddr_storage peer; struct sockaddr_storage peer2; char addr[INET6_ADDRSTRLEN]; char port[6]; if (where & SSL_CB_LOOP) { /* do not care for intermediary states */ return; } memset(&peer, 0, sizeof(peer)); (void) BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* lookup SSL object */ /* FIXME: need to get the ifindex */ idx = get_index_of_peer((struct sockaddr *)&peer, 0); if (idx >= 0) fprintf(stderr, "info_callback: assert: %d < 0 || %p == %p (storage: %p)\n", idx, ssl, ssl_peer_storage[idx]->ssl, ssl_peer_storage[idx]); if (idx >= 0 && ssl != ssl_peer_storage[idx]->ssl) { getnameinfo((struct sockaddr *)&peer, sizeof(peer), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr, " ssl: [%s]:%s ", addr, port); (void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[idx]->ssl), &peer2); getnameinfo((struct sockaddr *)&peer2, sizeof(peer2), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr, " ssl_peer_storage[idx]->ssl: [%s]:%s\n", addr, port); fprintf(stderr, " hash:%lu h: %lu\n", hash_peer((const struct sockaddr *)&peer, 0), ssl_peer_storage[idx]->h); for (i = 0; i < MAX_SSL_PEERS; i++) { if (ssl_peer_storage[i]) { fprintf(stderr, "%02d: %p ssl: %p ", i, ssl_peer_storage[i], ssl_peer_storage[i]->ssl); (void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[i]->ssl), &peer2); getnameinfo((struct sockaddr *)&peer2, sizeof(peer2), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr, " peer: [%s]:%s h: %lu\n", addr, port, ssl_peer_storage[i]->h); } } fprintf(stderr, "***** ASSERT FAILED ******\n"); memset(&peer, 0, sizeof(peer)); (void) BIO_dgram_get_peer(SSL_get_wbio(ssl), &peer); idx = get_index_of_peer((struct sockaddr *)&peer, 0); fprintf(stderr, " get_index_of_peer for wbio returns %d, type is %04x\n", idx, where); } #if 1 check_peers(); OPENSSL_assert((idx < 0) || (ssl == ssl_peer_storage[idx]->ssl)); #endif if (where & SSL_CB_ALERT) { #ifndef NDEBUG if (ret != 0) fprintf(stderr, "%s:%s:%s\n", SSL_alert_type_string(ret), SSL_alert_desc_string(ret), SSL_alert_desc_string_long(ret)); #endif /* examine alert type */ switch (*SSL_alert_type_string(ret)) { case 'F': /* move SSL object from pending to close */ if (idx >= 0) { ssl_peer_storage[idx]->state = PEER_ST_CLOSED; pending--; } break; case 'W': if ((ret & 0xff) == SSL_AD_CLOSE_NOTIFY) { if (where == SSL_CB_WRITE_ALERT) { fprintf(stderr, "sent CLOSE_NOTIFY\n"); } else { /* received CN */ fprintf(stderr, "received CLOSE_NOTIFY\n"); } } break; default: /* handle unknown alert types */ #ifndef NDEBUG printf("not handled!\n"); #endif } } if (where & SSL_CB_HANDSHAKE_DONE) { /* move SSL object from pending to established */ printf("HANDSHAKE_DONE "); if (idx >= 0) { if (ssl_peer_storage[idx]->state == PEER_ST_PENDING) { ssl_peer_storage[idx]->state = PEER_ST_ESTABLISHED; pending--; printf("moved SSL object %d to ESTABLISHED\n", idx); printf("%d objects pending\n", pending); } else { #ifndef NDEBUG printf("huh, object %d was not pending? (%d)\n", idx, ssl_peer_storage[idx]->state); #endif } return; } return; } return; } #endif #ifdef WITH_DTLS /* checks if ssl object was closed and can be removed */ int check_close(SSL *ssl) { int res, err, idx; struct sockaddr_storage peer; memset(&peer, 0, sizeof(peer)); (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); res = 0; if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) { printf("SSL_RECEIVED_SHUTDOWN\n"); res = SSL_shutdown(ssl); if (res == 0) { printf("must call SSL_shutdown again\n"); res = SSL_shutdown(ssl); } if (res < 0) { err = SSL_get_error(ssl, res); fprintf(stderr, "shutdown: SSL error %d: %s\n", err, ERR_error_string(err, NULL)); } /* we can close the SSL object anyway */ /* FIXME: need to get ifindex from somewhere */ idx = get_index_of_peer((struct sockaddr *)&peer, 0); OPENSSL_assert(idx < 0 || ssl == ssl_peer_storage[idx]->ssl); if (idx >= 0) { ssl_peer_storage[idx]->state = PEER_ST_CLOSED; printf("moved SSL object %d to CLOSED\n", idx); } } return res; } int check_timeout() { int i, result, err; for (i = 0; i < MAX_SSL_PEERS; i++) { if (ssl_peer_storage[i]) { OPENSSL_assert(ssl_peer_storage[i]->ssl); result = DTLSv1_handle_timeout(ssl_peer_storage[i]->ssl); if (result < 0) { err = SSL_get_error(ssl_peer_storage[i]->ssl, result); fprintf(stderr, "dtls1_handle_timeout (%d): %s\n", err, ERR_error_string(err, NULL)); } } } /* remove outdated obbjects? */ return 0; } #endif /* WITH_DTLS */ int _read(SSL_CTX *ctx, int sockfd) { char buf[2000]; struct sockaddr_in6 src; int len, ifindex, i; char addr[INET6_ADDRSTRLEN]; char port[6]; socklen_t sz = sizeof(struct sockaddr_in6); #ifdef WITH_DTLS SSL *ssl; int err; #endif /* Retrieve remote address and interface index as well as the first few bytes of the message to demultiplex protocols. */ memset(&src, 0, sizeof(struct sockaddr_in6)); len = check_connect(sockfd, buf, 4, (struct sockaddr *)&src, &ifindex); if (len < 0) { /* error */ return len; } #ifndef NDEBUG fprintf(stderr, "received packet"); if (getnameinfo((struct sockaddr *)&src, sizeof(src), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { fprintf(stderr, " from [%s]:%s", addr, port); } fprintf(stderr, " on interface %d\n", ifindex); #endif switch (demux_protocol(buf, len)) { #ifdef WITH_DTLS case DTLS : ssl = get_ssl(ctx, sockfd, (struct sockaddr *)&src, ifindex); if (!ssl) { fprintf(stderr, "cannot create new SSL object\n"); /* return recv(sockfd, buf, sizeof(buf), MSG_DONTWAIT);*/ len = recvfrom(sockfd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&src, &sz); getnameinfo((struct sockaddr *)&src, sz, addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); printf("discarded %d bytes from [%s]:%s\n", len, addr, port); return len; } len = SSL_read(ssl, buf, sizeof(buf)); break; #endif case UNKNOWN: default : len = recv(sockfd, buf, sizeof(buf), MSG_DONTWAIT); } if (len > 0) { printf("here is the data:\n"); for (i = 0; i < len; i++) { printf("%c", buf[i]); } } if (len == 0) { /* session closed? */ #ifdef WITH_DTLS if (check_close(ssl) <= 0) { fprintf(stderr, "not closed\n"); } #endif } else { #ifdef WITH_DTLS err = SSL_get_error(ssl, len); switch (err) { case SSL_ERROR_WANT_READ: fprintf(stderr, "SSL_ERROR_WANT_READ\n"); return 0; case SSL_ERROR_WANT_WRITE: fprintf(stderr, "SSL_ERROR_WANT_WRITE\n"); return 0; default: fprintf(stderr, "read: SSL error %d: %s\n", err, ERR_error_string(err, NULL)); return 0; } #else perror("recv"); #endif } return len; } int _write(SSL_CTX *ctx, int sockfd) { int res = 0; #ifdef WITH_DTLS SSL *ssl; int err; ssl = get_ssl(ctx, sockfd, NULL, 1); if (!ssl) { fprintf(stderr, "no SSL object for writing"); return 0; } res = SSL_write(ssl, NULL, 0); if (res < 0) { /* if (SSL_want_write(ssl)) return 0; */ /* FIXME: check SSL_want_read(ssl) */ err = SSL_get_error(ssl, res); fprintf(stderr, "SSL_write returned %d (%s)\n", err, ERR_error_string(err, NULL)); } else { printf("SSL_write successful\n"); } #else #endif return res; } int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) { /* FIXME: generate secure client-specific cookie */ #define DUMMYSTR "ABCDEFGHIJKLMNOP" *cookie_len = strlen(DUMMYSTR); memcpy(cookie, DUMMYSTR, *cookie_len); return 1; } int verify_cookie(SSL *ssl, unsigned char *cookie, unsigned int cookie_len) { /* FIXME */ return 1; } enum { READ, WRITE }; int main(int argc, char **argv) { int sockfd = 0; int on = 1; struct sockaddr_in6 listen_addr = { AF_INET6, htons(20220), 0, IN6ADDR_ANY_INIT, 0 }; size_t addr_size = sizeof(struct sockaddr_in6); fd_set fds[2]; int result, flags; int idx, res = 0; struct timeval timeout; struct sigaction act, oact; #ifdef WITH_DTLS SSL_CTX *ctx; memset(ssl_peer_storage, 0, sizeof(ssl_peer_storage)); SSL_load_error_strings(); SSL_library_init(); ctx = SSL_CTX_new(DTLSv1_server_method()); SSL_CTX_set_cipher_list(ctx, "ALL"); SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); res = SSL_CTX_use_certificate_file(ctx, SERVER_CERT_PEM, SSL_FILETYPE_PEM); if (res != 1) { fprintf(stderr, "cannot read server certificate from file '%s' (%s)\n", SERVER_CERT_PEM, ERR_error_string(res, NULL)); goto end; } res = SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY_PEM, SSL_FILETYPE_PEM); if (res != 1) { fprintf(stderr, "cannot read server key from file '%s' (%s)\n", SERVER_KEY_PEM, ERR_error_string(res, NULL)); goto end; } res = SSL_CTX_check_private_key(ctx); if (res != 1) { fprintf(stderr, "invalid private key\n"); goto end; } res = SSL_CTX_load_verify_locations(ctx, CA_CERT_PEM, NULL); if (res != 1) { fprintf(stderr, "cannot read ca file '%s'\n", CA_CERT_PEM); goto end; } /* Client has to authenticate */ /* Client has to authenticate */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL); SSL_CTX_set_read_ahead(ctx, 1); /* disable read-ahead */ SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie); SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie); SSL_CTX_use_psk_identity_hint(ctx, "Enter password for CoAP-Gateway"); SSL_CTX_set_psk_server_callback(ctx, psk_server_callback); SSL_CTX_set_info_callback(ctx, info_callback); #endif sockfd = socket(listen_addr.sin6_family, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); return -1; } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { perror("setsockopt SO_REUSEADDR"); } flags = fcntl(sockfd, F_GETFL, 0); if (flags < 0 || fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { perror("fcntl"); return -1; } on = 1; if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { perror("setsockopt IPV6_PKTINFO"); } if (bind(sockfd, (const struct sockaddr *)&listen_addr, addr_size) < 0) { perror("bind"); res = -2; goto end; } act.sa_handler = handle_sigint; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT, &act, &oact); while (!quit) { FD_ZERO(&fds[READ]); FD_ZERO(&fds[WRITE]); FD_SET(sockfd, &fds[READ]); timeout.tv_sec = 1; timeout.tv_usec = 0; result = select(FD_SETSIZE, &fds[READ], &fds[WRITE], 0, &timeout); if (result < 0) { /* error */ if (errno != EINTR) { perror("select"); } } else if (result > 0) { /* read from socket */ if (FD_ISSET(sockfd, &fds[READ])) { _read(ctx, sockfd); /* read received data */ } else if (FD_ISSET(sockfd, &fds[WRITE])) { /* write to socket */ _write(ctx, sockfd); /* write data */ } } else { /* timeout */ check_timeout(); } remove_closed(); } end: #ifdef WITH_DTLS for (idx = 0; idx < MAX_SSL_PEERS; idx++) { if (ssl_peer_storage[idx] && ssl_peer_storage[idx]->ssl) { if (ssl_peer_storage[idx]->state == PEER_ST_ESTABLISHED) { SSL_shutdown(ssl_peer_storage[idx]->ssl); } SSL_free(ssl_peer_storage[idx]->ssl); } } SSL_CTX_free(ctx); #endif close(sockfd); /* don't care if we close stdin at this point */ return res; }