int str_to_sockaddr( const char *src, sockaddr_union *dst) { int result; g_debug("parsing %s", src); /* try AF_INET first */ SU_INIT(dst, AF_INET); if ((result = inet_pton(AF_INET, src, &dst->sin.sin_addr)) == 1) return result; /* otherwise try AF_INET6, if supported */ #ifdef WORKING_IPV6 SU_INIT(dst, AF_INET6); return inet_pton(AF_INET6, src, &dst->sin6.sin6_addr); #else return result; #endif }
static sockaddr_union * unmap_v4mapped( sockaddr_union *sa, sockaddr_union *tmp) { if (SU_GET_FAMILY(sa) == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr)) { SU_INIT(tmp, AF_INET); SU_SET_PORT(tmp, SU_GET_PORT(sa)); /* extract the v4 address from byte 12 of the v6 address */ memcpy(&tmp->sin.sin_addr.s_addr, &sa->sin6.sin6_addr.s6_addr[12], sizeof(struct in_addr)); return tmp; } return sa; }
/* like sec_accept, but first it gets the remote system's hostname */ static void ssh_accept( const security_driver_t *driver, char *(*conf_fn)(char *, void *), int in, int out, void (*fn)(security_handle_t *, pkt_t *), void *datap) { struct sec_handle *rh; struct tcp_conn *rc = sec_tcp_conn_get(NULL, "", 0); char *ssh_connection, *p; char *errmsg = NULL; sockaddr_union addr; int result; /* "Accepting" an SSH connection means that amandad was invoked via sshd, so * we should have anSSH_CONNECTION env var. If not, then this probably isn't * a real SSH connection and we should bail out. */ ssh_connection = getenv("SSH_CONNECTION"); if (!ssh_connection) { errmsg = g_strdup("$SSH_CONNECTION not set - was amandad started by sshd?"); goto error; } /* make a local copy, to munge */ ssh_connection = g_strdup(ssh_connection); /* strip off the first component - the ASCII IP address */ if ((p = strchr(ssh_connection, ' ')) == NULL) { errmsg = g_strdup("$SSH_CONNECTION malformed"); goto error; } *p = '\0'; /* ---- everything from here on is just a warning, leaving hostname at "" */ SU_INIT(&addr, AF_INET); /* turn the string address into a sockaddr */ if ((result = str_to_sockaddr(ssh_connection, &addr)) != 1) { if (result == 0) { g_warning("Could not parse peer address %s", ssh_connection); } else { g_warning("Parsing peer address %s: %s", ssh_connection, gai_strerror(result)); } goto done; } /* find the hostname */ result = getnameinfo((struct sockaddr *)&addr, SS_LEN(&addr), rc->hostname, sizeof(rc->hostname), NULL, 0, 0); if (result != 0) { g_warning("Could not get hostname for SSH client %s: %s", ssh_connection, gai_strerror(result)); goto done; } /* and verify it */ if (check_name_give_sockaddr(rc->hostname, (struct sockaddr *)&addr, &errmsg) < 0) { rc->hostname[0] = '\0'; /* null out the bad hostname */ g_warning("Checking SSH client DNS: %s", errmsg); amfree(errmsg); goto done; } done: g_free(ssh_connection); rc->read = in; rc->write = out; rc->accept_fn = fn; rc->driver = driver; rc->conf_fn = conf_fn; rc->datap = datap; sec_tcp_conn_read(rc); return; error: if (ssh_connection) g_free(ssh_connection); /* make up a fake handle for the error */ rh = g_new0(struct sec_handle, 1); security_handleinit(&rh->sech, driver); security_seterror((security_handle_t*)rh, "ssh_accept: %s", errmsg); amfree(errmsg); (*fn)(&rh->sech, NULL); }
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; }
static int stream_client_internal( const char *hostname, in_port_t port, size_t sendsize, size_t recvsize, in_port_t *localport, int nonblock, int priv) { sockaddr_union svaddr, claddr; int save_errno = 0; int client_socket = 0; int *portrange = NULL; int result; struct addrinfo *res, *res_addr; result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL); if(result != 0) { g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result)); errno = EHOSTUNREACH; return -1; } if(!res) { g_debug(_("resolve_hostname(%s): no results"), hostname); errno = EHOSTUNREACH; return -1; } for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) { /* copy the first (preferred) address we found */ copy_sockaddr(&svaddr, (sockaddr_union *)res_addr->ai_addr); SU_SET_PORT(&svaddr, port); SU_INIT(&claddr, SU_GET_FAMILY(&svaddr)); SU_SET_INADDR_ANY(&claddr); /* * If a privileged port range was requested, we try to get a port in * that range first and fail if it is not available. Next, we try * to get a port in the range built in when Amanda was configured. * If that fails, we just go for any port. * * 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. */ if (priv) { portrange = getconf_intrange(CNF_RESERVED_TCP_PORT); } else { portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT); } client_socket = connect_portrange(&claddr, (in_port_t)portrange[0], (in_port_t)portrange[1], "tcp", &svaddr, nonblock); save_errno = errno; if (client_socket > 0) break; } freeaddrinfo(res); if (client_socket > 0) goto out; g_debug(_("stream_client: Could not bind to port in range %d-%d."), portrange[0], portrange[1]); errno = save_errno; return -1; out: try_socksize(client_socket, SO_SNDBUF, sendsize); try_socksize(client_socket, SO_RCVBUF, recvsize); if (localport != NULL) *localport = SU_GET_PORT(&claddr); return client_socket; }
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; }