/* * Write len bytes from buf to the socket. * Returns the return value from send() * Assert Class: 0 */ int sock_write_bytes(SOCKET sockfd, const char *buff, int len) { int t; if (!buff) { xa_debug(1, "ERROR: sock_write_bytes() called with NULL data"); return -1; } else if (len <= 0) { xa_debug(1, "ERROR: sock_write_bytes() called with zero or negative len"); return -1; } else if (!sock_valid(sockfd)) { xa_debug(1, "ERROR: sock_write_bytes() called with invalid socket"); return -1; } for(t=0 ; len > 0 ; ) { int n=send(sockfd, buff+t, len, 0); if (n < 0) return (t == 0) ? n : t; t+=n; len-=n; } return t; }
/* return 1 on success * return 0 on failure */ static int accept_connection(TCP_Server *TCP_server, sock_t sock) { if (!sock_valid(sock)) return 0; if (!set_socket_nonblock(sock)) { kill_sock(sock); return 0; } if (!set_socket_nosigpipe(sock)) { kill_sock(sock); return 0; } TCP_Secure_Connection *conn = &TCP_server->incomming_connection_queue[TCP_server->incomming_connection_queue_index % MAX_INCOMMING_CONNECTIONS]; if (conn->status != TCP_STATUS_NO_STATUS) kill_TCP_connection(conn); conn->status = TCP_STATUS_CONNECTED; conn->sock = sock; conn->next_packet_length = 0; ++TCP_server->incomming_connection_queue_index; return 1; }
static sock_t new_listening_TCP_socket(int family, uint16_t port) { sock_t sock = socket(family, SOCK_STREAM, IPPROTO_TCP); if (!sock_valid(sock)) { return ~0; } int ok = set_socket_nonblock(sock); if (ok && family == AF_INET6) { ok = set_socket_dualstack(sock); } if (ok) { ok = set_socket_reuseaddr(sock); } ok = ok && bind_to_port(sock, family, port) && (listen(sock, TCP_MAX_BACKLOG) == 0); if (!ok) { kill_sock(sock); return ~0; } return sock; }
int sock_del (SOCKET s) { ice_socket_t *is, *ds; xa_debug (3, "DEBUG: sock_del(): Removing socket %d", s); if (!sock_valid (s)) { xa_debug (1, "WARNING: Tried to remove invalid socket %d", s); return -1; } is = sock_find (s); if (!is) { xa_debug (1, "WARNING: Tried to remove a nonlisted socket %d", s); return -1; } thread_mutex_lock (&sock_mutex); ds = avl_delete (sock_sockets, is); if (ds) { nfree (ds); thread_mutex_unlock (&sock_mutex); return 1; } thread_mutex_unlock (&sock_mutex); return -1; }
/* return index on success * return -1 on failure */ static int accept_connection(TCP_Server *TCP_server, Socket sock) { if (!sock_valid(sock)) { return -1; } if (!set_socket_nonblock(sock)) { kill_sock(sock); return -1; } if (!set_socket_nosigpipe(sock)) { kill_sock(sock); return -1; } uint16_t index = TCP_server->incoming_connection_queue_index % MAX_INCOMING_CONNECTIONS; TCP_Secure_Connection *conn = &TCP_server->incoming_connection_queue[index]; if (conn->status != TCP_STATUS_NO_STATUS) { kill_TCP_secure_connection(conn); } conn->status = TCP_STATUS_CONNECTED; conn->sock = sock; conn->next_packet_length = 0; ++TCP_server->incoming_connection_queue_index; return index; }
static sock_t new_listening_TCP_socket(int family, uint16_t port) { sock_t sock = socket(family, SOCK_STREAM, IPPROTO_TCP); if (!sock_valid(sock)) { return ~0; } #ifndef TCP_SERVER_USE_EPOLL int ok = set_socket_nonblock(sock); #else int ok = 1; #endif if (ok && family == AF_INET6) { ok = set_socket_dualstack(sock); } ok = ok && bind_to_port(sock, family, port) && (listen(sock, TCP_MAX_BACKLOG) == 0); if (!ok) { kill_sock(sock); return ~0; } return sock; }
/* * Read a HTTP header. A number of lines terminated by a two newlines. * Reads no more than len bytes into string pointed to by buff. If the * return value is 1, then the string is valid and null terminated. * Assert Class: 3 * Potential Problems: Usage of errno */ int sock_read_lines_np(SOCKET sockfd, char *buff, const int len) { char c = '\0'; int read_bytes, pos = 0; if (!sock_valid(sockfd)) { xa_debug(1, "ERROR: sock_read_lines_np() called with invalid socket"); return 0; } else if (!buff) { xa_debug(1, "ERROR: sock_read_lines_np() called with NULL storage pointer"); return 0; } else if (len <= 0) { xa_debug(1, "ERROR: sock_read_lines_np() called with invalid length"); return 0; } #ifdef _WIN32 WSASetLastError(0); #else errno = 0; #endif read_bytes = recv(sockfd, &c, 1, 0); if (read_bytes < 0) { xa_debug(1, "DEBUG: Socket error on socket %d %d", sockfd, errno); return 0; } while ((read_bytes == 1) && (pos < (len - 1))) { if (c != '\r') buff[pos++] = c; if ((pos > 1) && (buff[pos - 1] == '\n' && buff[pos - 2] == '\n')) break; #ifdef _WIN32 WSASetLastError(0); #else errno = 0; #endif read_bytes = recv(sockfd, &c, 1, 0); if (read_bytes < 0) { xa_debug(1, "DEBUG: Socket error on socket %d %d", sockfd, errno); return 0; } } if (read_bytes == 1) { buff[pos] = '\0'; return 1; } else { return 0; } }
TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t *ports, uint8_t *public_key, uint8_t *secret_key, Onion *onion) { if (num_sockets == 0 || ports == NULL) return NULL; if (networking_at_startup() != 0) { return NULL; } TCP_Server *temp = calloc(1, sizeof(TCP_Server)); if (temp == NULL) return NULL; temp->socks_listening = calloc(num_sockets, sizeof(sock_t)); if (temp->socks_listening == NULL) { free(temp); return NULL; } uint8_t family; if (ipv6_enabled) { family = AF_INET6; } else { family = AF_INET; } uint32_t i; for (i = 0; i < num_sockets; ++i) { sock_t sock = new_listening_TCP_socket(family, ports[i]); if (sock_valid(sock)) { temp->socks_listening[temp->num_listening_socks] = sock; ++temp->num_listening_socks; } } if (temp->num_listening_socks == 0) { free(temp); return NULL; } if (onion) { temp->onion = onion; set_callback_handle_recv_1(onion, &handle_onion_recv_1, temp); } memcpy(temp->public_key, public_key, crypto_box_PUBLICKEYBYTES); memcpy(temp->secret_key, secret_key, crypto_box_SECRETKEYBYTES); return temp; }
/* * Set or the socket to blocking or nonblocking. * Assert Class: 1 */ int sock_set_blocking(SOCKET sockfd, const int block) { #ifdef _WIN32 int varblock = block; #else int res; #endif xa_debug(3, "Setting fd %d to %s", sockfd, (block == SOCK_BLOCK) ? "blocking" : "nonblocking"); if (!sock_valid(sockfd)) { xa_debug(1, "ERROR: sock_set_blocking() called with invalid socket"); return SOCKET_ERROR; } else if ((block < 0) || (block > 1)) { xa_debug(1, "ERROR: sock_set_blocking() called with invalid block value"); return SOCKET_ERROR; } #ifdef _WIN32 return ioctlsocket(sockfd, FIONBIO, &varblock); #else res = fcntl(sockfd, F_SETFL, (block == SOCK_BLOCK) ? 0 : O_NONBLOCK); if (res == -1) { xa_debug (1, "WARNING: sock_set_blocking() on socket %d failed", sockfd); return -1; } # ifdef DEBUG_SOCKETS { ice_socket_t *is = sock_find (sockfd); if (!is) { write_log (LOG_DEFAULT, "WARNING: sock_set_blocking(): Unknown socket %d", sockfd); return -1; } else { is->blocking = block; } } # endif return res; #endif }
TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t *ports, uint8_t *public_key, uint8_t *secret_key) { if (num_sockets == 0 || ports == NULL) return NULL; TCP_Server *temp = calloc(1, sizeof(TCP_Server)); if (temp == NULL) return NULL; temp->socks_listening = calloc(num_sockets, sizeof(sock_t)); if (temp->socks_listening == NULL) { free(temp); return NULL; } uint8_t family; if (ipv6_enabled) { family = AF_INET6; } else { family = AF_INET; } uint32_t i; for (i = 0; i < num_sockets; ++i) { sock_t sock = new_listening_TCP_socket(family, ports[i]); if (sock_valid(sock)) { temp->socks_listening[temp->num_listening_socks] = sock; ++temp->num_listening_socks; } } memcpy(temp->public_key, public_key, crypto_box_PUBLICKEYBYTES); memcpy(temp->secret_key, secret_key, crypto_box_SECRETKEYBYTES); return temp; }
/* * Write len bytes from buff to the client. Kick him on network errors. * Return the number of bytes written and -1 on error. * Assert Class: 2 * Potential problems: Any usage of errno is bad in a threaded application. */ int sock_write_bytes_or_kick(SOCKET sockfd, connection_t * clicon, const char *buff, const int len) { int res, err; if (!sock_valid(sockfd)) { xa_debug(1, "ERROR: sock_write_bytes_or_kick() called with invalid socket"); return -1; } else if (!clicon) { xa_debug(1, "ERROR: sock_write_bytes_or_kick() called with NULL client"); return -1; } else if (!buff) { xa_debug(1, "ERROR: sock_write_bytes_or_kick() called with NULL data"); return -1; } else if (len <= 0) { xa_debug(1, "ERROR: sock_write_bytes_or_kick() called with invalid length"); return -1; } errno = 666; res = sock_write_bytes(sockfd, buff, len); err = errno; if (res < 0) { xa_debug(4, "DEBUG: sock_write_bytes_or_kick: %d err [%d]", res, err); if (!is_recoverable(errno)) { kick_connection(clicon, "Client signed off"); return -1; } } return res; }
int sock_insert (ice_socket_t *is) { if (!is || !sock_valid (is->sock)) { write_log (LOG_DEFAULT, "WARNING: Tried to insert NULL socket"); return -1; } xa_debug (4, "DEBUG: inserting socket %d", is->sock); thread_mutex_lock (&sock_mutex); if (avl_insert (sock_sockets, is)) { write_log (LOG_DEFAULT, "WARNING: Tried to insert duplicate socket. Weird!"); thread_mutex_unlock (&sock_mutex); return 0; } thread_mutex_unlock (&sock_mutex); return 1; }
/* * Write a string to a socket. * Return 1 if all bytes where successfully written, and 0 if not. * Assert Class: 2 */ int sock_write_string(SOCKET sockfd, const char *buff) { int write_bytes = 0, res = 0, len = ice_strlen(buff); if (!sock_valid(sockfd)) { fprintf(stderr, "ERROR: sock_write_string() called with invalid socket\n"); return -1; } else if (!buff) { fprintf(stderr, "ERROR: sock_write_string() called with NULL format\n"); return -1; } /* * Never use send() to sockets 0 or 1 in Win32. */ if (sockfd == 1 || sockfd == 0) { if (running == SERVER_RUNNING) { write_bytes = fprintf(stdout, "%s", buff); fflush(stdout); } } else { while (write_bytes < len) { res = send(sockfd, &buff[write_bytes], len - write_bytes, 0); if (res < 0 && !is_recoverable(errno)) return 0; if (res > 0) write_bytes += res; else my_sleep(30000); } } return (write_bytes == len ? 1 : 0); }
SOCKET sock_accept(SOCKET s, struct sockaddr * addr, socklen_t * addrlen) { SOCKET rs = accept(s, addr, addrlen); xa_debug (4, "DEBUG: sock_accept() created socket %d", s); if (sock_valid(rs)) { #ifdef DEBUG_SOCKETS sock_add (rs, AF_INET, SOCK_STREAM, 0); #endif /* * Turn on KEEPALIVE to detect crashed hosts */ sock_set_keepalive(rs, 1); #ifdef SO_LINGER sock_set_no_linger(rs); #endif } return rs; }
SOCKET sock_socket(int domain, int type, int protocol) { SOCKET s = socket(domain, type, protocol); xa_debug (4, "DEBUG: sock_socket() creating socket %d", s); if (sock_valid(s)) { #ifdef DEBUG_SOCKETS sock_add (s, domain, type, protocol); #endif /* * Turn on KEEPALIVE to detect crashed hosts */ sock_set_keepalive(s, 1); #ifdef SO_LINGER sock_set_no_linger(s); #endif } return s; }
static void do_TCP_epoll(TCP_Server *TCP_server) { #define MAX_EVENTS 16 struct epoll_event events[MAX_EVENTS]; int nfds; while ((nfds = epoll_wait(TCP_server->efd, events, MAX_EVENTS, 0)) > 0) { int n; for (n = 0; n < nfds; ++n) { sock_t sock = events[n].data.u64 & 0xFFFFFFFF; int status = (events[n].data.u64 >> 32) & 0xFF, index = (events[n].data.u64 >> 40); if ((events[n].events & EPOLLERR) || (events[n].events & EPOLLHUP) || (events[n].events & EPOLLRDHUP)) { switch (status) { case TCP_SOCKET_LISTENING: { //should never happen break; } case TCP_SOCKET_INCOMING: { kill_TCP_connection(&TCP_server->incomming_connection_queue[index]); break; } case TCP_SOCKET_UNCONFIRMED: { kill_TCP_connection(&TCP_server->unconfirmed_connection_queue[index]); break; } case TCP_SOCKET_CONFIRMED: { kill_accepted(TCP_server, index); break; } } continue; } if (!(events[n].events & EPOLLIN)) { continue; } switch (status) { case TCP_SOCKET_LISTENING: { //socket is from socks_listening, accept connection struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); while (1) { sock_t sock_new = accept(sock, (struct sockaddr *)&addr, &addrlen); if (!sock_valid(sock_new)) { break; } int index_new = accept_connection(TCP_server, sock_new); if (index_new == -1) { continue; } struct epoll_event ev = { .events = EPOLLIN | EPOLLET | EPOLLRDHUP, .data.u64 = sock_new | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40) }; if (epoll_ctl(TCP_server->efd, EPOLL_CTL_ADD, sock_new, &ev) == -1) { kill_TCP_connection(&TCP_server->incomming_connection_queue[index_new]); continue; } } break; } case TCP_SOCKET_INCOMING: { int index_new; if ((index_new = do_incoming(TCP_server, index)) != -1) { events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP; events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 40); if (epoll_ctl(TCP_server->efd, EPOLL_CTL_MOD, sock, &events[n]) == -1) { kill_TCP_connection(&TCP_server->unconfirmed_connection_queue[index_new]); break; } } break; } case TCP_SOCKET_UNCONFIRMED: { int index_new; if ((index_new = do_unconfirmed(TCP_server, index)) != -1) { events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP; events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 40); if (epoll_ctl(TCP_server->efd, EPOLL_CTL_MOD, sock, &events[n]) == -1) { //remove from confirmed connections kill_accepted(TCP_server, index_new); break; } } break; } case TCP_SOCKET_CONFIRMED: { do_confirmed_recv(TCP_server, index); break; } } } } #undef MAX_EVENTS } #endif void do_TCP_server(TCP_Server *TCP_server) { unix_time_update(); #ifdef TCP_SERVER_USE_EPOLL do_TCP_epoll(TCP_server); #else do_TCP_accept_new(TCP_server); do_TCP_incomming(TCP_server); do_TCP_unconfirmed(TCP_server); #endif do_TCP_confirmed(TCP_server); }
connection_t * get_connection (sock_t *sock) { int sockfd; socklen_t sin_len; connection_t *con; fd_set rfds; struct timeval tv; int i, maxport = 0; struct sockaddr_in *sin = (struct sockaddr_in *)nmalloc(sizeof(struct sockaddr_in)); if (!sin) { write_log (LOG_DEFAULT, "WARNING: Weird stuff in get_connection. nmalloc returned NULL sin"); return NULL; } /* setup sockaddr structure */ sin_len = sizeof(struct sockaddr_in); memset(sin, 0, sin_len); /* try to accept a connection */ FD_ZERO(&rfds); for (i = 0; i < MAXLISTEN; i++) { if (sock_valid (sock[i])) { FD_SET(sock[i], &rfds); if (sock[i] > maxport) maxport = sock[i]; } } maxport += 1; tv.tv_sec = 0; tv.tv_usec = 30000; if (select(maxport, &rfds, NULL, NULL, &tv) > 0) { for (i = 0; i < MAXLISTEN; i++) { if (sock_valid (sock[i]) && FD_ISSET(sock[i], &rfds)) break; } } else { nfree(sin); return NULL; } sockfd = sock_accept(sock[i], (struct sockaddr *)sin, &sin_len); if (sockfd >= 0) { con = create_connection(); if (!sin) { xa_debug (1, "ERROR: NULL sockaddr struct, wft???"); return NULL; } con->host = create_malloced_ascii_host(&(sin->sin_addr)); con->sock = sockfd; con->sin = sin; con->sinlen = sin_len; xa_debug (2, "DEBUG: Getting new connection on socket %d from host %s", sockfd, con->host ? con->host : "(null)"); con->hostname = NULL; con->headervars = NULL; con->id = new_id (); con->connect_time = get_time (); #ifdef HAVE_LIBWRAP if (!sock_check_libwrap(sockfd, unknown_connection_e)) { kick_not_connected (con, "Access Denied (tcp wrappers) [generic connection]"); return NULL; } #endif return con; } if (!is_recoverable (errno)) xa_debug (1, "WARNING: accept() failed with on socket %d, max: %d, [%d:%s]", sock[i], maxport, errno, strerror(errno)); nfree (sin); return NULL; }
TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, const uint8_t *public_key, const uint8_t *secret_key, Onion *onion) { if (num_sockets == 0 || ports == NULL) return NULL; if (networking_at_startup() != 0) { return NULL; } TCP_Server *temp = calloc(1, sizeof(TCP_Server)); if (temp == NULL) return NULL; temp->socks_listening = calloc(num_sockets, sizeof(sock_t)); if (temp->socks_listening == NULL) { free(temp); return NULL; } #ifdef TCP_SERVER_USE_EPOLL temp->efd = epoll_create1(0); if (temp->efd == -1) { free(temp); return NULL; } #endif uint8_t family; if (ipv6_enabled) { family = AF_INET6; } else { family = AF_INET; } uint32_t i; #ifdef TCP_SERVER_USE_EPOLL struct epoll_event ev; #endif for (i = 0; i < num_sockets; ++i) { sock_t sock = new_listening_TCP_socket(family, ports[i]); if (sock_valid(sock)) { #ifdef TCP_SERVER_USE_EPOLL ev.events = EPOLLIN; ev.data.u64 = sock | ((uint64_t)TCP_SOCKET_LISTENING << 32); if (epoll_ctl(temp->efd, EPOLL_CTL_ADD, sock, &ev) == -1) { continue; } #endif temp->socks_listening[temp->num_listening_socks] = sock; ++temp->num_listening_socks; } } if (temp->num_listening_socks == 0) { free(temp); return NULL; } if (onion) { temp->onion = onion; set_callback_handle_recv_1(onion, &handle_onion_recv_1, temp); } memcpy(temp->public_key, public_key, crypto_box_PUBLICKEYBYTES); memcpy(temp->secret_key, secret_key, crypto_box_SECRETKEYBYTES); bs_list_init(&temp->accepted_key_list, crypto_box_PUBLICKEYBYTES, 8); return temp; }
/* Create new TCP connection to ip_port/public_key */ TCP_Client_Connection *new_TCP_connection(IP_Port ip_port, const uint8_t *public_key, const uint8_t *self_public_key, const uint8_t *self_secret_key, TCP_Proxy_Info *proxy_info) { if (networking_at_startup() != 0) { return NULL; } if (ip_port.ip.family != AF_INET && ip_port.ip.family != AF_INET6) { return NULL; } uint8_t family = ip_port.ip.family; TCP_Proxy_Info default_proxyinfo; if (proxy_info == NULL) { default_proxyinfo.proxy_type = TCP_PROXY_NONE; proxy_info = &default_proxyinfo; } if (proxy_info->proxy_type != TCP_PROXY_NONE) { family = proxy_info->ip_port.ip.family; } Socket sock = net_socket(family, TOX_SOCK_STREAM, TOX_PROTO_TCP); if (!sock_valid(sock)) { return NULL; } if (!set_socket_nosigpipe(sock)) { kill_sock(sock); return 0; } if (!(set_socket_nonblock(sock) && connect_sock_to(sock, ip_port, proxy_info))) { kill_sock(sock); return NULL; } TCP_Client_Connection *temp = (TCP_Client_Connection *)calloc(sizeof(TCP_Client_Connection), 1); if (temp == NULL) { kill_sock(sock); return NULL; } temp->sock = sock; memcpy(temp->public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); memcpy(temp->self_public_key, self_public_key, CRYPTO_PUBLIC_KEY_SIZE); encrypt_precompute(temp->public_key, self_secret_key, temp->shared_key); temp->ip_port = ip_port; temp->proxy_info = *proxy_info; switch (proxy_info->proxy_type) { case TCP_PROXY_HTTP: temp->status = TCP_CLIENT_PROXY_HTTP_CONNECTING; proxy_http_generate_connection_request(temp); break; case TCP_PROXY_SOCKS5: temp->status = TCP_CLIENT_PROXY_SOCKS5_CONNECTING; proxy_socks5_generate_handshake(temp); break; case TCP_PROXY_NONE: temp->status = TCP_CLIENT_CONNECTING; if (generate_handshake(temp) == -1) { kill_sock(sock); free(temp); return NULL; } break; } temp->kill_at = unix_time() + TCP_CONNECTION_TIMEOUT; return temp; }