/************************************************************************** Try to connect to a server (get_server_address() must be called first!): - try to create a TCP socket and connect it to `server_addr' - if successful: - start monitoring the socket for packets from the server - send a "login request" packet to the server and - return 0 - if unable to create the connection, close the socket, put an error message in ERRBUF and return the Unix error code (ie., errno, which will be non-zero). **************************************************************************/ static int try_to_connect(const char *username, char *errbuf, int errbufsize) { close_socket_set_callback(close_socket_callback); /* connection in progress? wait. */ if (client.conn.used) { (void) mystrlcpy(errbuf, _("Connection in progress."), errbufsize); return -1; } if ((client.conn.sock = socket(server_addr.saddr.sa_family, SOCK_STREAM, 0)) == -1) { (void) mystrlcpy(errbuf, fc_strerror(fc_get_errno()), errbufsize); return -1; } if (fc_connect(client.conn.sock, &server_addr.saddr, sockaddr_size(&server_addr)) == -1) { (void) mystrlcpy(errbuf, fc_strerror(fc_get_errno()), errbufsize); fc_closesocket(client.conn.sock); client.conn.sock = -1; #ifdef HAVE_WINSOCK return -1; #else return errno; #endif } make_connection(client.conn.sock, username); return 0; }
/************************************************************************* Writes buf to socket and returns the response in an fz_FILE. Use only on blocking sockets. *************************************************************************/ fz_FILE *fc_querysocket(int sock, void *buf, size_t size) { FILE *fp; #ifdef HAVE_FDOPEN fp = fdopen(sock, "r+b"); if (fwrite(buf, 1, size, fp) != size) { log_error("socket %d: write error", sock); } fflush(fp); /* we don't use fc_closesocket on sock here since when fp is closed * sock will also be closed. fdopen doesn't dup the socket descriptor. */ #else /* HAVE_FDOPEN */ { char tmp[4096]; int n; #ifdef WIN32_NATIVE /* tmpfile() in mingw attempts to make a temp file in the root directory * of the current drive, which we may not have write access to. */ { char filename[MAX_PATH]; GetTempPath(sizeof(filename), filename); sz_strlcat(filename, "fctmp"); fp = fc_fopen(filename, "w+b"); } #else /* WIN32_NATIVE */ fp = tmpfile(); #endif /* WIN32_NATIVE */ if (fp == NULL) { return NULL; } fc_writesocket(sock, buf, size); while ((n = fc_readsocket(sock, tmp, sizeof(tmp))) > 0) { if (fwrite(tmp, 1, n, fp) != n) { log_error("socket %d: write error", sock); } } fflush(fp); fc_closesocket(sock); rewind(fp); } #endif /* HAVE_FDOPEN */ return fz_from_stream(fp); }
/************************************************************************** Try to connect to a server (get_server_address() must be called first!): - try to create a TCP socket and connect it to `names' - if successful: - start monitoring the socket for packets from the server - send a "login request" packet to the server and - return 0 - if unable to create the connection, close the socket, put an error message in ERRBUF and return the Unix error code (ie., errno, which will be non-zero). **************************************************************************/ static int try_to_connect(const char *username, char *errbuf, int errbufsize) { int i; int sock = -1; connections_set_close_callback(client_conn_close_callback); /* connection in progress? wait. */ if (client.conn.used) { (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize); return -1; } /* Try all (IPv4, IPv6, ...) addresses until we have a connection. */ sock = -1; for (i = 0; i < name_count; i++) { if ((sock = socket(names[i].saddr.sa_family, SOCK_STREAM, 0)) == -1) { /* Probably EAFNOSUPPORT or EPROTONOSUPPORT. */ continue; } if (fc_connect(sock, &names[i].saddr, sockaddr_size(&names[i])) == -1) { fc_closesocket(sock); sock = -1; continue; } else { /* We have a connection! */ break; } } client.conn.sock = sock; if (client.conn.sock == -1) { fc_errno err = fc_get_errno(); /* Save errno value before calling anything */ (void) fc_strlcpy(errbuf, fc_strerror(err), errbufsize); #ifdef HAVE_WINSOCK return -1; #else return err; #endif /* HAVE_WINSOCK */ } make_connection(client.conn.sock, username); return 0; }
/**************************************************************************** Begin a metaserver scan for servers. This just initiates the connection to the metaserver; later get_meta_server_list should be called whenever the socket has data pending to read and parse it. Returns FALSE on error (in which case errbuf will contain an error message). ****************************************************************************/ static bool begin_metaserver_scan(struct server_scan *scan) { union fc_sockaddr addr; int s; scan->meta.urlpath = fc_lookup_httpd(scan->meta.name, &scan->meta.port, metaserver); if (!scan->meta.urlpath) { scan->error_func(scan, _("Invalid $http_proxy or metaserver value, must " "start with 'http://'")); return FALSE; } if (!net_lookup_service(scan->meta.name, scan->meta.port, &addr, FALSE)) { scan->error_func(scan, _("Failed looking up metaserver's host")); return FALSE; } if ((s = socket(addr.saddr.sa_family, SOCK_STREAM, 0)) == -1) { scan->error_func(scan, fc_strerror(fc_get_errno())); return FALSE; } fc_nonblock(s); if (fc_connect(s, &addr.saddr, sockaddr_size(&addr)) == -1) { if (errno == EINPROGRESS) { /* With non-blocking sockets this is the expected result. */ scan->meta.state = META_CONNECTING; scan->sock = s; } else { fc_closesocket(s); scan->error_func(scan, fc_strerror(fc_get_errno())); return FALSE; } } else { /* Instant connection? Whoa. */ scan->sock = s; scan->meta.state = META_CONNECTING; meta_send_request(scan); } return TRUE; }
/************************************************************************** Finds the next (lowest) free port. **************************************************************************/ int find_next_free_port(int starting_port) { int port, s = socket(AF_INET, SOCK_STREAM, 0); for (port = starting_port;; port++) { union fc_sockaddr tmp; struct sockaddr_in *sock = &tmp.saddr_in4; memset(&tmp, 0, sizeof(tmp)); sock->sin_family = AF_INET; sock->sin_port = htons(port); sock->sin_addr.s_addr = htonl(INADDR_ANY); if (bind(s, &tmp.saddr, sockaddr_size(&tmp)) == 0) { break; } } fc_closesocket(s); return port; }
/************************************************************************** Finds the next (lowest) free port. **************************************************************************/ int find_next_free_port(int starting_port, enum fc_addr_family family) { int port; int s; int gafamily; bool found = FALSE; #ifndef IPV6_SUPPORT fc_assert(family == FC_ADDR_IPV4 || family == FC_ADDR_ANY); #endif switch (family) { case FC_ADDR_IPV4: gafamily = AF_INET; break; #ifdef IPV6_SUPPORT case FC_ADDR_IPV6: gafamily = AF_INET6; break; #endif /* IPV6_SUPPORT */ case FC_ADDR_ANY: gafamily = AF_UNSPEC; break; default: fc_assert(FALSE); return -1; } s = socket(gafamily, SOCK_STREAM, 0); for (port = starting_port; !found ; port++) { /* HAVE_GETADDRINFO implies IPv6 support */ #ifdef HAVE_GETADDRINFO struct addrinfo hints; int err; char servname[8]; struct addrinfo *res; fc_snprintf(servname, sizeof(servname), "%d", port); memset(&hints, 0, sizeof(hints)); hints.ai_family = gafamily; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE | FC_AI_NUMERICSERV; err = getaddrinfo(NULL, servname, &hints, &res); if (!err) { struct addrinfo *current = res; while (current != NULL && !found) { if (bind(s, current->ai_addr, current->ai_addrlen) == 0) { found = TRUE; } current = current->ai_next; } freeaddrinfo(res); } #else /* HAVE_GETADDRINFO */ union fc_sockaddr tmp; struct sockaddr_in *sock4; sock4 = &tmp.saddr_in4; memset(&tmp, 0, sizeof(tmp)); sock4->sin_family = AF_INET; sock4->sin_port = htons(port); sock4->sin_addr.s_addr = htonl(INADDR_ANY); if (bind(s, &tmp.saddr, sockaddr_size(&tmp)) == 0) { found = TRUE; } #endif /* HAVE_GETADDRINFO */ } fc_closesocket(s); return port; }
/************************************************************************** Finds the next (lowest) free port. **************************************************************************/ int find_next_free_port(int starting_port, int highest_port, enum fc_addr_family family, char *net_interface, bool not_avail_ok) { int port; int s; int gafamily; bool found = FALSE; #ifndef IPV6_SUPPORT fc_assert(family == FC_ADDR_IPV4 || family == FC_ADDR_ANY); #endif switch (family) { case FC_ADDR_IPV4: gafamily = AF_INET; break; #ifdef IPV6_SUPPORT case FC_ADDR_IPV6: gafamily = AF_INET6; break; #endif /* IPV6_SUPPORT */ case FC_ADDR_ANY: gafamily = AF_UNSPEC; break; default: fc_assert(FALSE); log_error("Port from unsupported address family requested!"); return -1; } for (port = starting_port; !found && highest_port > port; port++) { /* HAVE_GETADDRINFO implies IPv6 support */ #ifdef HAVE_GETADDRINFO struct addrinfo hints; int err; char servname[8]; struct addrinfo *res; fc_snprintf(servname, sizeof(servname), "%d", port); memset(&hints, 0, sizeof(hints)); hints.ai_family = gafamily; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE | FC_AI_NUMERICSERV; err = getaddrinfo(net_interface, servname, &hints, &res); if (!err) { struct addrinfo *current = res; bool unusable = FALSE; while (current != NULL && !unusable) { s = socket(current->ai_family, SOCK_STREAM, 0); if (s == -1) { log_error("socket(): %s", fc_strerror(fc_get_errno())); } else { if (bind(s, current->ai_addr, current->ai_addrlen) != 0) { if (!not_avail_ok || fc_get_errno() != EADDRNOTAVAIL) { unusable = TRUE; } } } current = current->ai_next; fc_closesocket(s); } freeaddrinfo(res); if (!unusable && res != NULL) { found = TRUE; } } #else /* HAVE_GETADDRINFO */ union fc_sockaddr tmp; struct sockaddr_in *sock4; s = socket(gafamily, SOCK_STREAM, 0); sock4 = &tmp.saddr_in4; memset(&tmp, 0, sizeof(tmp)); sock4->sin_family = AF_INET; sock4->sin_port = htons(port); if (net_interface != NULL) { #if defined(HAVE_INET_ATON) if (inet_aton(net_interface, &sock4->sin_addr) == 0) { #else /* HAVE_INET_ATON */ sock4->sin_addr.s_addr = inet_addr(net_interface); if (sock4->sin_addr.s_addr == INADDR_NONE) { #endif /* HAVE_INET_ATON */ struct hostent *hp; hp = gethostbyname(net_interface); if (hp == NULL) { log_error("No hostent for %s!", net_interface); return -1; } if (hp->h_addrtype != AF_INET) { log_error("Requested IPv4 address for %s, got something else! (%d)", net_interface, hp->h_addrtype); return -1; } memcpy(&sock4->sin_addr, hp->h_addr, hp->h_length); } } else { sock4->sin_addr.s_addr = htonl(INADDR_ANY); } if (bind(s, &tmp.saddr, sockaddr_size(&tmp)) == 0) { found = TRUE; } fc_closesocket(s); #endif /* HAVE_GETADDRINFO */ } if (!found) { log_error("None of the ports %d - %d is available.", starting_port, highest_port); return -1; } /* Rollback the last increment from the loop, back to the port * number found to be free. */ port--; return port; }