static gint sock_connect_by_hostname(gint sock, const gchar *hostname, gushort port) { struct hostent *hp; struct sockaddr_in ad; resolver_init(); memset(&ad, 0, sizeof(ad)); ad.sin_family = AF_INET; ad.sin_port = htons(port); if (!my_inet_aton(hostname, &ad.sin_addr)) { if ((hp = my_gethostbyname(hostname)) == NULL) { fprintf(stderr, "%s: unknown host.\n", hostname); errno = 0; return -1; } if (hp->h_length != 4 && hp->h_length != 8) { fprintf(stderr, "illegal address length received for host %s\n", hostname); errno = 0; return -1; } memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); } return sock_connect_with_timeout(sock, (struct sockaddr *)&ad, sizeof(ad), io_timeout); }
static SockDesc sock_connect_by_getaddrinfo(const gchar *hostname, gushort port) { SockDesc sock = INVALID_SOCKET; gint gai_error; struct addrinfo hints, *res, *ai; gchar port_str[6]; resolver_init(); memset(&hints, 0, sizeof(hints)); /* hints.ai_flags = AI_CANONNAME; */ hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; /* convert port from integer to string. */ g_snprintf(port_str, sizeof(port_str), "%d", port); if ((gai_error = getaddrinfo(hostname, port_str, &hints, &res)) != 0) { fprintf(stderr, "getaddrinfo for %s:%s failed: %s\n", hostname, port_str, gai_strerror(gai_error)); return INVALID_SOCKET; } for (ai = res; ai != NULL; ai = ai->ai_next) { sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (!SOCKET_IS_VALID(sock)) continue; sock_set_buffer_size(sock); if (sock_connect_with_timeout (sock, ai->ai_addr, ai->ai_addrlen, io_timeout) == 0) break; fd_close(sock); } if (res != NULL) freeaddrinfo(res); if (ai == NULL) return INVALID_SOCKET; return sock; }
/* Due to the fact that socket under Windows are not represented by standard file descriptors, we sometimes need to check whether a given file descriptor is actually a socket. This is done by testing for an error. Returns true under W32 if FD is a socket. */ static int fd_is_w32_socket(gint fd) { #ifdef G_OS_WIN32 gint optval; gint retval = sizeof(optval); return !getsockopt(fd, SOL_SOCKET, SO_TYPE, (char*)&optval, &retval); #else return 0; #endif } gint fd_connect_inet(gushort port) { gint sock; struct sockaddr_in addr; sock = socket(AF_INET, SOCK_STREAM, 0); if (!SOCKET_IS_VALID(sock)) { #ifdef G_OS_WIN32 debug_print("fd_connect_inet(): socket() failed: %d\n", WSAGetLastError()); #else perror("fd_connect_inet(): socket"); #endif return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { fd_close(sock); return -1; } return sock; } gint fd_open_inet(gushort port) { gint sock; struct sockaddr_in addr; gint val; sock = socket(AF_INET, SOCK_STREAM, 0); if (!SOCKET_IS_VALID(sock)) { #ifdef G_OS_WIN32 g_warning("fd_open_inet(): socket() failed: %d\n", WSAGetLastError()); #else perror("fd_open_inet(): socket"); #endif return -1; } val = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)) < 0) { perror("setsockopt"); fd_close(sock); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); fd_close(sock); return -1; } if (listen(sock, 1) < 0) { perror("listen"); fd_close(sock); return -1; } return sock; } gint fd_connect_unix(const gchar *path) { #ifdef G_OS_UNIX gint sock; struct sockaddr_un addr; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("sock_connect_unix(): socket"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(sock); return -1; } return sock; #else return -1; #endif } gint fd_open_unix(const gchar *path) { #ifdef G_OS_UNIX gint sock; struct sockaddr_un addr; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("sock_open_unix(): socket"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { gchar *buf = g_strdup_printf("can't bind to %s", path); perror(buf); g_free(buf); close(sock); return -1; } if (listen(sock, 1) < 0) { gchar *buf = g_strdup_printf("can't listen on %s", path); perror(buf); g_free(buf); close(sock); return -1; } return sock; #else return -1; #endif } gint fd_accept(gint sock) { struct sockaddr_in caddr; guint caddr_len; caddr_len = sizeof(caddr); return accept(sock, (struct sockaddr *)&caddr, &caddr_len); } static gint set_nonblocking_mode(gint fd, gboolean nonblock) { #ifdef G_OS_UNIX gint flags; flags = fcntl(fd, F_GETFL, 0); if (flags < 0) { perror("fcntl"); return -1; } if (nonblock) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; return fcntl(fd, F_SETFL, flags); #else return -1; #endif } gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock) { cm_return_val_if_fail(sock != NULL, -1); return set_nonblocking_mode(sock->sock, nonblock); } static gboolean is_nonblocking_mode(gint fd) { #ifdef G_OS_UNIX gint flags; flags = fcntl(fd, F_GETFL, 0); if (flags < 0) { perror("fcntl"); return FALSE; } return ((flags & O_NONBLOCK) != 0); #else return FALSE; #endif } gboolean sock_is_nonblocking_mode(SockInfo *sock) { cm_return_val_if_fail(sock != NULL, FALSE); return is_nonblocking_mode(sock->sock); } #ifdef USE_GNUTLS static gboolean ssl_sock_prepare(GSource *source, gint *timeout) { *timeout = 1; return FALSE; } static gboolean ssl_sock_check(GSource *source) { SockInfo *sock = ((SockSource *)source)->sock; struct timeval timeout = {0, 0}; fd_set fds; GIOCondition condition = 0; if (!sock || !sock->sock) return FALSE; condition = sock->condition; if ((condition & G_IO_IN) == G_IO_IN && gnutls_record_check_pending(sock->ssl) != 0) return TRUE; FD_ZERO(&fds); FD_SET(sock->sock, &fds); select(sock->sock + 1, (condition & G_IO_IN) ? &fds : NULL, (condition & G_IO_OUT) ? &fds : NULL, NULL, &timeout); return FD_ISSET(sock->sock, &fds) != 0; } static gboolean ssl_sock_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { SockInfo *sock = ((SockSource *)source)->sock; if (!sock || !sock->callback || !sock->data) return FALSE; return sock->callback(sock, sock->condition, sock->data); } #endif static gboolean sock_watch_cb(GIOChannel *source, GIOCondition condition, gpointer data) { SockInfo *sock = (SockInfo *)data; if ((condition & sock->condition) == 0) return TRUE; return sock->callback(sock, sock->condition, sock->data); } guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func, gpointer data) { if (!sock) return FALSE; sock->callback = func; sock->condition = condition; sock->data = data; #ifdef USE_GNUTLS if (sock->ssl) { GSource *source = g_source_new(&ssl_watch_funcs, sizeof(SockSource)); ((SockSource *) source)->sock = sock; g_source_set_priority(source, G_PRIORITY_DEFAULT); g_source_set_can_recurse(source, FALSE); sock->g_source = g_source_attach(source, NULL); g_source_unref (source); /* Refcount back down to 1 */ return sock->g_source; } #endif return g_io_add_watch(sock->sock_ch, condition, sock_watch_cb, sock); } static gint fd_check_io(gint fd, GIOCondition cond) { struct timeval timeout; fd_set fds; if (is_nonblocking_mode(fd)) return 0; timeout.tv_sec = io_timeout; timeout.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); if (cond == G_IO_IN) { select(fd + 1, &fds, NULL, NULL, io_timeout > 0 ? &timeout : NULL); } else { select(fd + 1, NULL, &fds, NULL, io_timeout > 0 ? &timeout : NULL); } if (FD_ISSET(fd, &fds)) { return 0; } else { g_warning("Socket IO timeout\n"); log_error(LOG_PROTOCOL, _("Socket IO timeout.\n")); return -1; } } #ifdef G_OS_UNIX static sigjmp_buf jmpenv; static void timeout_handler(gint sig) { siglongjmp(jmpenv, 1); } #endif /*G_OS_UNIX*/ static gint sock_connect_with_timeout(gint sock, const struct sockaddr *serv_addr, gint addrlen, guint timeout_secs) { gint ret; #ifdef G_OS_UNIX void (*prev_handler)(gint); alarm(0); prev_handler = signal(SIGALRM, timeout_handler); if (sigsetjmp(jmpenv, 1)) { alarm(0); signal(SIGALRM, prev_handler); errno = ETIMEDOUT; log_error(LOG_PROTOCOL, _("Connection timed out.\n")); return -1; } alarm(timeout_secs); #endif ret = connect(sock, serv_addr, addrlen); #ifdef G_OS_UNIX alarm(0); signal(SIGALRM, prev_handler); #endif return ret; } struct hostent *my_gethostbyname(const gchar *hostname) { struct hostent *hp; #ifdef G_OS_UNIX void (*prev_handler)(gint); alarm(0); prev_handler = signal(SIGALRM, timeout_handler); if (sigsetjmp(jmpenv, 1)) { alarm(0); signal(SIGALRM, prev_handler); g_printerr("%s: host lookup timed out.\n", hostname); log_error(LOG_PROTOCOL, _("%s: host lookup timed out.\n"), hostname); errno = 0; return NULL; } alarm(io_timeout); #endif if ((hp = gethostbyname(hostname)) == NULL) { #ifdef G_OS_UNIX alarm(0); signal(SIGALRM, prev_handler); #endif g_printerr("%s: unknown host.\n", hostname); log_error(LOG_PROTOCOL, _("%s: unknown host.\n"), hostname); errno = 0; return NULL; } #ifdef G_OS_UNIX alarm(0); signal(SIGALRM, prev_handler); #endif return hp; } #ifndef INET6 static gint my_inet_aton(const gchar *hostname, struct in_addr *inp) { #if HAVE_INET_ATON return inet_aton(hostname, inp); #else #if HAVE_INET_ADDR guint32 inaddr; inaddr = inet_addr(hostname); if (inaddr != -1) { memcpy(inp, &inaddr, sizeof(inaddr)); return 1; } else return 0; #else return 0; #endif #endif /* HAVE_INET_ATON */ } static gint sock_connect_by_hostname(gint sock, const gchar *hostname, gushort port) { struct hostent *hp; struct sockaddr_in ad; memset(&ad, 0, sizeof(ad)); ad.sin_family = AF_INET; ad.sin_port = htons(port); refresh_resolvers(); if (!my_inet_aton(hostname, &ad.sin_addr)) { if ((hp = my_gethostbyname(hostname)) == NULL) { g_printerr("%s: unknown host.\n", hostname); errno = 0; return -1; } if (hp->h_length != 4 && hp->h_length != 8) { g_printerr("illegal address length received for host %s\n", hostname); errno = 0; return -1; } memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); } return sock_connect_with_timeout(sock, (struct sockaddr *)&ad, sizeof(ad), io_timeout); } #else /* INET6 */ static gint sock_connect_by_getaddrinfo(const gchar *hostname, gushort port) { gint sock = -1, gai_error; struct addrinfo hints, *res, *ai; gchar port_str[6]; refresh_resolvers(); memset(&hints, 0, sizeof(hints)); /* hints.ai_flags = AI_CANONNAME; */ hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; /* convert port from integer to string. */ g_snprintf(port_str, sizeof(port_str), "%d", port); if ((gai_error = getaddrinfo(hostname, port_str, &hints, &res)) != 0) { g_printerr("getaddrinfo for %s:%s failed: %s\n", hostname, port_str, gai_strerror(gai_error)); return -1; } for (ai = res; ai != NULL; ai = ai->ai_next) { sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) continue; if (sock_connect_with_timeout (sock, ai->ai_addr, ai->ai_addrlen, io_timeout) == 0) break; close(sock); } if (res != NULL) freeaddrinfo(res); if (ai == NULL) return -1; return sock; }