int stream_accept( int server_socket, int timeout, size_t sendsize, size_t recvsize) { time_t timeout_time; int connected_socket; int save_errno; in_port_t port; assert(server_socket >= 0); /* set the time we want to stop accepting */ timeout_time = time(NULL) + timeout; while(1) { addrlen = (socklen_t_equiv)sizeof(sockaddr_union); connected_socket = interruptible_accept(server_socket, (struct sockaddr *)&addr, &addrlen, stream_accept_prolong, &timeout_time); if(connected_socket < 0) { if (errno == 0) { g_debug(plural(_("stream_accept: timeout after %d second"), _("stream_accept: timeout after %d seconds"), timeout), timeout); errno = ETIMEDOUT; return -1; } break; } g_debug(_("stream_accept: connection from %s"), str_sockaddr(&addr)); /* * Make certain we got an inet connection and that it is not * from port 20 (a favorite unauthorized entry tool). */ if (SU_GET_FAMILY(&addr) == AF_INET #ifdef WORKING_IPV6 || SU_GET_FAMILY(&addr) == AF_INET6 #endif ){ port = SU_GET_PORT(&addr); if (port != (in_port_t)20) { try_socksize(connected_socket, SO_SNDBUF, sendsize); try_socksize(connected_socket, SO_RCVBUF, recvsize); return connected_socket; } else { g_debug(_("remote port is %u: ignored"), (unsigned int)port); } } else { #ifdef WORKING_IPV6 g_debug(_("family is %d instead of %d(AF_INET)" " or %d(AF_INET6): ignored"), SU_GET_FAMILY(&addr), AF_INET, AF_INET6); #else g_debug(_("family is %d instead of %d(AF_INET)" ": ignored"), SU_GET_FAMILY(&addr), AF_INET); #endif } aclose(connected_socket); } save_errno = errno; g_debug(_("stream_accept: accept() failed: %s"), strerror(save_errno)); errno = save_errno; return -1; }
int stream_server( int family, in_port_t *portp, size_t sendsize, size_t recvsize, int priv) { int server_socket, retries; socklen_t_equiv len; #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR) const int on = 1; int r; #endif sockaddr_union server; int save_errno; int *portrange; socklen_t_equiv socklen; int socket_family; *portp = USHRT_MAX; /* in case we error exit */ if (family == -1) { socket_family = AF_NATIVE; } else { socket_family = family; } g_debug("stream_server opening socket with family %d (requested family was %d)", socket_family, family); server_socket = socket(socket_family, SOCK_STREAM, 0); #ifdef WORKING_IPV6 /* if that address family actually isn't supported, just try AF_INET */ if (server_socket == -1 && errno == EAFNOSUPPORT) { g_debug("stream_server retrying socket with AF_INET"); socket_family = AF_INET; server_socket = socket(AF_INET, SOCK_STREAM, 0); } #endif if (server_socket == -1) { save_errno = errno; g_debug(_("stream_server: socket() failed: %s"), strerror(save_errno)); errno = save_errno; return -1; } if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) { aclose(server_socket); errno = EMFILE; /* out of range */ save_errno = errno; g_debug(_("stream_server: socket out of range: %d"), server_socket); errno = save_errno; return -1; } SU_INIT(&server, socket_family); SU_SET_INADDR_ANY(&server); #ifdef USE_REUSEADDR r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&on, (socklen_t_equiv)sizeof(on)); if (r < 0) { g_debug(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s"), strerror(errno)); } #endif try_socksize(server_socket, SO_SNDBUF, sendsize); try_socksize(server_socket, SO_RCVBUF, recvsize); /* * If a port range was specified, we try to get a port in that * range first. Next, we try to get a reserved port. If that * fails, we just go for any port. * * In all cases, not to use port that's assigned to other services. * * It is up to the caller to make sure we have the proper permissions * to get the desired port, and to make sure we return a port that * is within the range it requires. */ for (retries = 0; ; retries++) { if (priv) { portrange = getconf_intrange(CNF_RESERVED_TCP_PORT); } else { portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT); } if (portrange[0] != 0 && portrange[1] != 0) { if (bind_portrange(server_socket, &server, (in_port_t)portrange[0], (in_port_t)portrange[1], "tcp") == 0) goto out; g_debug(_("stream_server: Could not bind to port in range: %d - %d."), portrange[0], portrange[1]); } else { socklen = SS_LEN(&server); if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0) goto out; g_debug(_("stream_server: Could not bind to any port: %s"), strerror(errno)); } if (retries >= BIND_CYCLE_RETRIES) break; g_debug(_("stream_server: Retrying entire range after 10 second delay.")); sleep(15); } save_errno = errno; g_debug(_("stream_server: bind(in6addr_any) failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; out: listen(server_socket, 1); /* find out what port was actually used */ len = sizeof(server); if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) { save_errno = errno; g_debug(_("stream_server: getsockname() failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; } #ifdef SO_KEEPALIVE r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, (socklen_t_equiv)sizeof(on)); if(r == -1) { save_errno = errno; g_debug(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; } #endif *portp = SU_GET_PORT(&server); g_debug(_("stream_server: waiting for connection: %s"), str_sockaddr(&server)); return server_socket; }
/* * Open a ssl connection to the host listed in rc->hostname * Returns negative on error, with an errmsg in rc->errmsg. */ static int runssl( struct sec_handle * rh, in_port_t port, char *src_ip, char *ssl_fingerprint_file, char *ssl_cert_file, char *ssl_key_file, char *ssl_ca_cert_file, char *ssl_cipher_list, int ssl_check_certificate_host) { int my_socket; in_port_t my_port; struct tcp_conn *rc = rh->rc; int err; X509 *remote_cert; sockaddr_union sin; socklen_t_equiv len; if (!ssl_key_file) { security_seterror(&rh->sech, _("ssl-key-file must be set")); return -1; } if (!ssl_cert_file) { security_seterror(&rh->sech, _("ssl-cert-file must be set")); return -1; } my_socket = stream_client(src_ip, rc->hostname, port, STREAM_BUFSIZE, STREAM_BUFSIZE, &my_port, 0); if(my_socket < 0) { security_seterror(&rh->sech, "%s", strerror(errno)); return -1; } rc->read = rc->write = my_socket; len = sizeof(sin); if (getpeername(my_socket, (struct sockaddr *)&sin, &len) < 0) { security_seterror(&rh->sech, _("getpeername returned: %s\n"), strerror(errno)); return -1; } copy_sockaddr(&rc->peer, &sin); init_ssl(); /* Create an SSL_CTX structure */ rc->ctx = SSL_CTX_new(SSLv3_client_method()); if (!rc->ctx) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } SSL_CTX_set_mode(rc->ctx, SSL_MODE_AUTO_RETRY); if (ssl_cipher_list) { g_debug("Set ssl_cipher_list to %s", ssl_cipher_list); if (SSL_CTX_set_cipher_list(rc->ctx, ssl_cipher_list) == 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } } /* Load the private-key corresponding to the remote certificate */ g_debug("Loading ssl-key-file private-key %s", ssl_key_file); if (SSL_CTX_use_PrivateKey_file(rc->ctx, ssl_key_file, SSL_FILETYPE_PEM) <= 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Load the me certificate into the SSL_CTX structure */ g_debug("Loading ssl-cert-file certificate %s", ssl_cert_file); if (SSL_CTX_use_certificate_file(rc->ctx, ssl_cert_file, SSL_FILETYPE_PEM) <= 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Check if the remote certificate and private-key matches */ if (ssl_cert_file) { if (!SSL_CTX_check_private_key(rc->ctx)) { security_seterror(&rh->sech, _("Private key does not match the certificate public key")); return -1; } } if (ssl_ca_cert_file) { /* Load the RSA CA certificate into the SSL_CTX structure */ /* This will allow this remote to verify the me's */ /* certificate. */ g_debug("Loading ssl-ca-cert-file ca %s", ssl_ca_cert_file); if (!SSL_CTX_load_verify_locations(rc->ctx, ssl_ca_cert_file, NULL)) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } } else { g_debug(_("no ssl-ca-cert-file defined")); } /* Set flag in context to require peer (me) certificate */ /* verification */ if (ssl_ca_cert_file) { g_debug("Enabling certification verification"); SSL_CTX_set_verify(rc->ctx, SSL_VERIFY_PEER, NULL); SSL_CTX_set_verify_depth(rc->ctx, 1); } else { g_debug("Not enabling certification verification"); } /* ----------------------------------------------- */ rc->ssl = SSL_new(rc->ctx); if (!rc->ssl) { security_seterror(&rh->sech, _("SSL_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return -1; } SSL_set_connect_state(rc->ssl); /* Assign the socket into the SSL structure (SSL and socket without BIO) */ SSL_set_fd(rc->ssl, my_socket); /* Perform SSL Handshake on the SSL remote */ err = SSL_connect(rc->ssl); if (err == -1) { security_seterror(&rh->sech, _("SSL_connect failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Get the me's certificate (optional) */ remote_cert = SSL_get_peer_certificate(rc->ssl); if (remote_cert == NULL) { security_seterror(&rh->sech, _("server have no certificate")); return -1; } else { char *str; str = X509_NAME_oneline(X509_get_subject_name(remote_cert), 0, 0); auth_debug(1, _("\t subject: %s\n"), str); amfree (str); str = X509_NAME_oneline(X509_get_issuer_name(remote_cert), 0, 0); auth_debug(1, _("\t issuer: %s\n"), str); amfree(str); if (ssl_check_certificate_host) { int loc = -1; char *errmsg = NULL; X509_NAME *x509_name = X509_get_subject_name(remote_cert); loc = X509_NAME_get_index_by_NID(x509_name, NID_commonName, loc); if (loc != -1) { X509_NAME_ENTRY *x509_entry = X509_NAME_get_entry(x509_name, loc); ASN1_STRING *asn1_string = X509_NAME_ENTRY_get_data(x509_entry); char *cert_hostname = (char *)ASN1_STRING_data(asn1_string); auth_debug(1, "common_name: %s\n", cert_hostname); if (check_name_give_sockaddr((char*)cert_hostname, (struct sockaddr *)&rc->peer, &errmsg) < 0) { security_seterror(&rh->sech, _("Common name of certicate (%s) doesn't resolv to IP (%s): %s"), cert_hostname, str_sockaddr(&rc->peer), errmsg); amfree(errmsg); return -1; } auth_debug(1, _("Certificate common name (%s) resolve to IP (%s)\n"), cert_hostname, str_sockaddr(&rc->peer)); } else { security_seterror(&rh->sech, _("Certificate have no common name")); g_debug("Certificate have no common name"); return -1; } } /* if (ssl_dir) { if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') { struct stat statbuf; ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, cert_hostname); if (stat(ssl_fingerprint_file, &statbuf) == -1) { g_free(ssl_fingerprint_file); ssl_fingerprint_file = NULL; } } } */ if (ssl_fingerprint_file) { g_debug(_("run_ssl: Loading ssl-fingerprint-file %s"), ssl_fingerprint_file); str = validate_fingerprints(remote_cert, ssl_fingerprint_file); if (str) { security_seterror(&rh->sech, "%s", str); amfree(str); return -1; } } X509_free (remote_cert); } g_debug(_("SSL_cipher: %s"), SSL_get_cipher(rc->ssl)); return 0; }
/* * Setup to handle new incoming connections */ static void ssl_accept( const struct security_driver *driver, char * (*conf_fn)(char *, void *), int in, int out, void (*fn)(security_handle_t *, pkt_t *), void *datap) { sockaddr_union sin; socklen_t_equiv len = sizeof(struct sockaddr); struct tcp_conn *rc; char hostname[NI_MAXHOST]; int result; char *errmsg = NULL; int err; X509 *remote_cert; char *str; X509_NAME *x509_name; char *cert_hostname; SSL_CTX *ctx; SSL *ssl; int loc; char *ssl_dir = getconf_str(CNF_SSL_DIR); char *ssl_fingerprint_file = conf_fn("ssl_fingerprint_file", datap); char *ssl_cert_file = conf_fn("ssl_cert_file", datap); char *ssl_key_file = conf_fn("ssl_key_file", datap); char *ssl_ca_cert_file = conf_fn("ssl_ca_cert_file", datap); char *ssl_cipher_list = conf_fn("ssl_cipher_list", datap); int ssl_check_host = atoi(conf_fn("ssl_check_host", datap)); int ssl_check_certificate_host = atoi(conf_fn("ssl_check_certificate_host", datap)); if (getpeername(in, (struct sockaddr *)&sin, &len) < 0) { g_debug(_("getpeername returned: %s"), strerror(errno)); return; } if ((result = getnameinfo((struct sockaddr *)&sin, len, hostname, NI_MAXHOST, NULL, 0, 0) != 0)) { g_debug(_("getnameinfo failed: %s"), gai_strerror(result)); return; } if (ssl_check_host && check_name_give_sockaddr(hostname, (struct sockaddr *)&sin, &errmsg) < 0) { amfree(errmsg); return; } if (ssl_dir) { if (!ssl_cert_file || ssl_cert_file == '\0') { ssl_cert_file = g_strdup_printf("%s/me/crt.pem", ssl_dir); } if (!ssl_key_file || ssl_key_file == '\0') { ssl_key_file = g_strdup_printf("%s/me/private/key.pem", ssl_dir); } if (!ssl_ca_cert_file || ssl_ca_cert_file == '\0') { ssl_ca_cert_file = g_strdup_printf("%s/CA/crt.pem", ssl_dir); } } if (!ssl_cert_file) { g_debug(_("ssl-cert-file must be set in amanda-remote.conf")); return; } if (!ssl_key_file) { g_debug(_("ssl-key-file must be set in amanda-remote.conf")); return; } if (!ssl_ca_cert_file) { g_debug(_("ssl_ca_cert_file must be set in amanda-remote.conf")); return; } len = sizeof(sin); init_ssl(); /* Create a SSL_CTX structure */ ctx = SSL_CTX_new(SSLv3_server_method()); if (!ctx) { g_debug(_("SSL_CTX_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); if (ssl_cipher_list) { g_debug("Set ssl_cipher_list to %s", ssl_cipher_list); if (SSL_CTX_set_cipher_list(ctx, ssl_cipher_list) == 0) { g_debug(_("SSL_CTX_set_cipher_list failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } } /* Load the me certificate into the SSL_CTX structure */ g_debug(_("Loading ssl-cert-file certificate %s"), ssl_cert_file); if (SSL_CTX_use_certificate_file(ctx, ssl_cert_file, SSL_FILETYPE_PEM) <= 0) { g_debug(_("Load ssl-cert-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Load the private-key corresponding to the me certificate */ g_debug(_("Loading ssl-key-file private-key %s"), ssl_key_file); if (SSL_CTX_use_PrivateKey_file(ctx, ssl_key_file, SSL_FILETYPE_PEM) <= 0) { g_debug(_("Load ssl-key-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } if (ssl_ca_cert_file) { /* Load the RSA CA certificate into the SSL_CTX structure */ g_debug(_("Loading ssl-ca-cert-file ca certificate %s"), ssl_ca_cert_file); if (!SSL_CTX_load_verify_locations(ctx, ssl_ca_cert_file, NULL)) { g_debug(_("Load ssl-ca-cert-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Set to require peer (remote) certificate verification */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); /* Set the verification depth to 1 */ SSL_CTX_set_verify_depth(ctx,1); } ssl = SSL_new(ctx); if (!ssl) { g_debug(_("SSL_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } SSL_set_accept_state(ssl); /* Assign the socket into the SSL structure (SSL and socket without BIO) */ SSL_set_fd(ssl, in); /* Perform SSL Handshake on the SSL me */ err = SSL_accept(ssl); if (err == -1) { g_debug(_("SSL_accept failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Get the me's certificate (optional) */ remote_cert = SSL_get_peer_certificate (ssl); if (remote_cert == NULL) { g_debug(_("remote doesn't sent a certificate")); return; } x509_name = X509_get_subject_name(remote_cert); str = X509_NAME_oneline(X509_get_subject_name(remote_cert), 0, 0); auth_debug(1, _("\t subject: %s\n"), str); amfree (str); str = X509_NAME_oneline(X509_get_issuer_name(remote_cert), 0, 0); auth_debug(1, _("\t issuer: %s\n"), str); amfree(str); loc = -1; loc = X509_NAME_get_index_by_NID(x509_name, NID_commonName, loc); if (loc != -1) { X509_NAME_ENTRY *x509_entry = X509_NAME_get_entry(x509_name, loc); ASN1_STRING *asn1_string = X509_NAME_ENTRY_get_data(x509_entry); cert_hostname = (char *)ASN1_STRING_data(asn1_string); auth_debug(1, "common_name: %s\n", cert_hostname); if (ssl_check_certificate_host && check_name_give_sockaddr((char*)cert_hostname, (struct sockaddr *)&sin, &errmsg) < 0) { g_debug("Common name of certicate (%s) doesn't resolv to IP (%s)", cert_hostname, str_sockaddr(&sin)); amfree(errmsg); X509_free(remote_cert); return; } } else { g_debug("Certificate have no common name"); X509_free(remote_cert); return; } if (ssl_dir) { if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') { struct stat statbuf; ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, cert_hostname); if (stat(ssl_fingerprint_file, &statbuf) == -1) { g_free(ssl_fingerprint_file); ssl_fingerprint_file = NULL; } } } if (ssl_fingerprint_file) { g_debug(_("Loading ssl-fingerprint-file %s"), ssl_fingerprint_file); str = validate_fingerprints(remote_cert, ssl_fingerprint_file); if (str) { g_debug("%s", str); amfree(str); X509_free(remote_cert); return; } } X509_free(remote_cert); rc = sec_tcp_conn_get(hostname, 0); rc->recv_security_ok = &bsd_recv_security_ok; rc->prefix_packet = &bsd_prefix_packet; rc->need_priv_port = 0; copy_sockaddr(&rc->peer, &sin); rc->read = in; rc->write = out; rc->accept_fn = fn; rc->driver = driver; rc->conf_fn = conf_fn; rc->datap = datap; rc->ctx = ctx; rc->ssl = ssl; strncpy(rc->hostname, cert_hostname, sizeof(rc->hostname)-1); g_debug(_("SSL_cipher: %s"), SSL_get_cipher(rc->ssl)); sec_tcp_conn_read(rc); }
/* return >0: this is the connected socket */ int connect_port( sockaddr_union *addrp, in_port_t port, char * proto, sockaddr_union *svaddr, int nonblock) { int save_errno; struct servent * servPort; socklen_t_equiv len; socklen_t_equiv socklen; int s; servPort = getservbyport((int)htons(port), proto); if (servPort != NULL && !strstr(servPort->s_name, AMANDA_SERVICE_NAME)) { dbprintf(_("connect_port: Skip port %d: owned by %s.\n"), port, servPort->s_name); errno = EBUSY; return -1; } if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2; SU_SET_PORT(addrp, port); socklen = SS_LEN(addrp); if (bind(s, (struct sockaddr *)addrp, socklen) != 0) { save_errno = errno; aclose(s); if(servPort == NULL) { dbprintf(_("connect_port: Try port %d: available - %s\n"), port, strerror(errno)); } else { dbprintf(_("connect_port: Try port %d: owned by %s - %s\n"), port, servPort->s_name, strerror(errno)); } if (save_errno != EADDRINUSE) { errno = save_errno; return -2; } errno = save_errno; return -1; } if(servPort == NULL) { dbprintf(_("connect_port: Try port %d: available - Success\n"), port); } else { dbprintf(_("connect_port: Try port %d: owned by %s - Success\n"), port, servPort->s_name); } /* find out what port was actually used */ len = sizeof(*addrp); if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) { save_errno = errno; dbprintf(_("connect_port: getsockname() failed: %s\n"), strerror(save_errno)); aclose(s); errno = save_errno; return -1; } if (nonblock) { int r = fcntl(s, F_GETFL, 0); if (r < 0) { save_errno = errno; g_debug("Can't fcntl(F_GETFL): %s", strerror(errno)); aclose(s); errno = save_errno; return -1; } r = fcntl(s, F_SETFL, r|O_NONBLOCK); if (r < 0) { save_errno = errno; g_debug("Can't fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); errno = save_errno; aclose(s); return -1; } } if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) { save_errno = errno; dbprintf(_("connect_portrange: Connect from %s failed: %s\n"), str_sockaddr(addrp), strerror(save_errno)); dbprintf(_("connect_portrange: connect to %s failed: %s\n"), str_sockaddr(svaddr), strerror(save_errno)); aclose(s); errno = save_errno; if (save_errno == ECONNREFUSED || save_errno == EHOSTUNREACH || save_errno == ENETUNREACH || save_errno == ETIMEDOUT) { return -2 ; } return -1; } dbprintf(_("connected to %s\n"), str_sockaddr(svaddr)); dbprintf(_("our side is %s\n"), str_sockaddr(addrp)); return s; }
int dgram_bind( dgram_t * dgram, sa_family_t family, in_port_t * portp) { int s, retries; socklen_t_equiv len; sockaddr_union name; int save_errno; int *portrange; int sndbufsize = MAX_DGRAM; portrange = getconf_intrange(CNF_RESERVED_UDP_PORT); *portp = (in_port_t)0; g_debug("dgram_bind: setting up a socket with family %d", family); if((s = socket(family, SOCK_DGRAM, 0)) == -1) { save_errno = errno; dbprintf(_("dgram_bind: socket() failed: %s\n"), strerror(save_errno)); errno = save_errno; return -1; } if(s < 0 || s >= (int)FD_SETSIZE) { dbprintf(_("dgram_bind: socket out of range: %d\n"), s); aclose(s); errno = EMFILE; /* out of range */ return -1; } /* try setting the buffer size (= maximum allowable UDP packet size) */ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbufsize, sizeof(sndbufsize)) < 0) { dbprintf("dgram_bind: could not set udp send buffer to %d: %s (ignored)\n", sndbufsize, strerror(errno)); } SU_INIT(&name, family); SU_SET_INADDR_ANY(&name); /* * If a port range was specified, we try to get a port in that * range first. Next, we try to get a reserved port. If that * fails, we just go for any port. * * In all cases, not to use port that's assigned to other services. * * It is up to the caller to make sure we have the proper permissions * to get the desired port, and to make sure we return a port that * is within the range it requires. */ for (retries = 0; ; retries++) { if (bind_portrange(s, &name, portrange[0], portrange[1], "udp") == 0) goto out; dbprintf(_("dgram_bind: Could not bind to port in range: %d - %d.\n"), portrange[0], portrange[1]); if (retries >= BIND_CYCLE_RETRIES) { dbprintf(_("dgram_bind: Giving up...\n")); break; } dbprintf(_("dgram_bind: Retrying entire range after 10 second delay.\n")); sleep(15); } save_errno = errno; dbprintf(_("dgram_bind: bind(in6addr_any) failed: %s\n"), strerror(save_errno)); aclose(s); errno = save_errno; return -1; out: /* find out what name was actually used */ len = (socklen_t_equiv)sizeof(name); if(getsockname(s, (struct sockaddr *)&name, &len) == -1) { save_errno = errno; dbprintf(_("dgram_bind: getsockname() failed: %s\n"), strerror(save_errno)); errno = save_errno; aclose(s); return -1; } *portp = SU_GET_PORT(&name); dgram->socket = s; dbprintf(_("dgram_bind: socket %d bound to %s\n"), dgram->socket, str_sockaddr(&name)); return 0; }
int dgram_send_addr( sockaddr_union *addr, dgram_t * dgram) { int s, rc; int socket_opened; int save_errno; int max_wait; int wait_count; #if defined(USE_REUSEADDR) const int on = 1; int r; #endif dbprintf(_("dgram_send_addr(addr=%p, dgram=%p)\n"), addr, dgram); dump_sockaddr(addr); dbprintf(_("dgram_send_addr: %p->socket = %d\n"), dgram, dgram->socket); if(dgram->socket != -1) { s = dgram->socket; socket_opened = 0; } else { int sndbufsize = MAX_DGRAM; g_debug("dgram_send_addr: setting up a socket with family %d", SU_GET_FAMILY(addr)); if((s = socket(SU_GET_FAMILY(addr), SOCK_DGRAM, 0)) == -1) { save_errno = errno; dbprintf(_("dgram_send_addr: socket() failed: %s\n"), strerror(save_errno)); errno = save_errno; return -1; } socket_opened = 1; #ifdef USE_REUSEADDR r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&on, (socklen_t_equiv)sizeof(on)); if (r < 0) { dbprintf(_("dgram_send_addr: setsockopt(SO_REUSEADDR) failed: %s\n"), strerror(errno)); } #endif /* try setting the buffer size (= maximum allowable UDP packet size) */ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbufsize, sizeof(sndbufsize)) < 0) { dbprintf("dgram_send_addr: could not set udp send buffer to %d: %s (ignored)\n", sndbufsize, strerror(errno)); } } if(s < 0 || s >= (int)FD_SETSIZE) { dbprintf(_("dgram_send_addr: socket out of range: %d\n"), s); errno = EMFILE; /* out of range */ rc = -1; } else { max_wait = 300 / 5; /* five minutes */ wait_count = 0; rc = 0; while(sendto(s, dgram->data, dgram->len, 0, (struct sockaddr *)addr, SS_LEN(addr)) == -1) { #ifdef ECONNREFUSED if(errno == ECONNREFUSED && wait_count++ < max_wait) { sleep(5); dbprintf(_("dgram_send_addr: sendto(%s): retry %d after ECONNREFUSED\n"), str_sockaddr(addr), wait_count); continue; } #endif #ifdef EAGAIN if(errno == EAGAIN && wait_count++ < max_wait) { sleep(5); dbprintf(_("dgram_send_addr: sendto(%s): retry %d after EAGAIN\n"), str_sockaddr(addr), wait_count); continue; } #endif save_errno = errno; dbprintf(_("dgram_send_addr: sendto(%s) failed: %s \n"), str_sockaddr(addr), strerror(save_errno)); errno = save_errno; rc = -1; break; } } if(socket_opened) { save_errno = errno; if(close(s) == -1) { dbprintf(_("dgram_send_addr: close(%s): failed: %s\n"), str_sockaddr(addr), strerror(errno)); /* * Calling function should not care that the close failed. * It does care about the send results though. */ } errno = save_errno; } return rc; }