/********************************************************************* * * Function : acl_addr * * Description : Called from `load_config' to parse an ACL address. * * Parameters : * 1 : aspec = String specifying ACL address. * 2 : aca = struct access_control_addr to fill in. * * Returns : 0 => Ok, everything else is an error. * *********************************************************************/ int acl_addr(char *aspec, struct access_control_addr *aca) { int i, masklength, port; char *p; masklength = 32; port = 0; if ((p = strchr(aspec, '/')) != NULL) { *p++ = '\0'; if (ijb_isdigit(*p) == 0) { return(-1); } masklength = atoi(p); } if ((masklength < 0) || (masklength > 32)) { return(-1); } if ((p = strchr(aspec, ':')) != NULL) { *p++ = '\0'; if (ijb_isdigit(*p) == 0) { return(-1); } port = atoi(p); } aca->port = port; aca->addr = ntohl(resolve_hostname_to_ip(aspec)); if (aca->addr == INADDR_NONE) { return(-1); } /* build the netmask */ aca->mask = 0; for (i=1; i <= masklength ; i++) { aca->mask |= (1 << (32 - i)); } /* now mask off the host portion of the ip address * (i.e. save on the network portion of the address). */ aca->addr = aca->addr & aca->mask; return(0); }
/********************************************************************* * * Function : bind_port * * Description : Call socket, set socket options, and listen. * Called by listen_loop to "boot up" our proxy address. * * Parameters : * 1 : hostnam = TCP/IP address to bind/listen to * 2 : portnum = port to listen on * 3 : pfd = pointer used to return file descriptor. * * Returns : if success, returns 0 and sets *pfd. * if failure, returns -3 if address is in use, * -2 if address unresolvable, * -1 otherwise *********************************************************************/ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) { #ifdef HAVE_RFC2553 struct addrinfo hints; struct addrinfo *result, *rp; /* * XXX: portnum should be a string to allow symbolic service * names in the configuration file and to avoid the following * int2string. */ char servnam[6]; int retval; #else struct sockaddr_in inaddr; #endif /* def HAVE_RFC2553 */ jb_socket fd = JB_INVALID_SOCKET; #ifndef _WIN32 int one = 1; #endif /* ndef _WIN32 */ *pfd = JB_INVALID_SOCKET; #ifdef HAVE_RFC2553 retval = snprintf(servnam, sizeof(servnam), "%d", portnum); if ((-1 == retval) || (sizeof(servnam) <= retval)) { log_error(LOG_LEVEL_ERROR, "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes", portnum); return -1; } memset(&hints, 0, sizeof(struct addrinfo)); if (hostnam == NULL) { /* * XXX: This is a hack. The right thing to do * would be to bind to both AF_INET and AF_INET6. * This will also fail if there is no AF_INET * version available. */ hints.ai_family = AF_INET; } else { hints.ai_family = AF_UNSPEC; } hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; hints.ai_protocol = 0; /* Really any stream protocol or TCP only */ hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; if ((retval = getaddrinfo(hostnam, servnam, &hints, &result))) { log_error(LOG_LEVEL_ERROR, "Can not resolve %s: %s", hostnam, gai_strerror(retval)); return -2; } #else memset((char *)&inaddr, '\0', sizeof inaddr); inaddr.sin_family = AF_INET; inaddr.sin_addr.s_addr = resolve_hostname_to_ip(hostnam); if (inaddr.sin_addr.s_addr == INADDR_NONE) { return(-2); } #ifndef _WIN32 if (sizeof(inaddr.sin_port) == sizeof(short)) #endif /* ndef _WIN32 */ { inaddr.sin_port = htons((unsigned short) portnum); } #ifndef _WIN32 else { inaddr.sin_port = htonl((unsigned long) portnum); } #endif /* ndef _WIN32 */ #endif /* def HAVE_RFC2553 */ #ifdef HAVE_RFC2553 for (rp = result; rp != NULL; rp = rp->ai_next) { fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); #else fd = socket(AF_INET, SOCK_STREAM, 0); #endif /* def HAVE_RFC2553 */ #ifdef _WIN32 if (fd == JB_INVALID_SOCKET) #else if (fd < 0) #endif { #ifdef HAVE_RFC2553 continue; #else return(-1); #endif } #ifdef FEATURE_EXTERNAL_FILTERS mark_socket_for_close_on_execute(fd); #endif #ifndef _WIN32 /* * This is not needed for Win32 - in fact, it stops * duplicate instances of Privoxy from being caught. * * On UNIX, we assume the user is sensible enough not * to start Privoxy multiple times on the same IP. * Without this, stopping and restarting Privoxy * from a script fails. * Note: SO_REUSEADDR is meant to only take over * sockets which are *not* in listen state in Linux, * e.g. sockets in TIME_WAIT. YMMV. */ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); setsockopt (fd, SOL_SOCKET, SO_NOSIGPIPE, (char *)&one, sizeof(one)); #endif /* ndef _WIN32 */ #ifdef HAVE_RFC2553 if (bind(fd, rp->ai_addr, rp->ai_addrlen) < 0) #else if (bind(fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0) #endif { #ifdef _WIN32 errno = WSAGetLastError(); if (errno == WSAEADDRINUSE) #else if (errno == EADDRINUSE) #endif { #ifdef HAVE_RFC2553 freeaddrinfo(result); #endif close_socket(fd); return(-3); } else { close_socket(fd); #ifndef HAVE_RFC2553 return(-1); } } #else } }
static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct client_state *csp) { struct sockaddr_in inaddr; jb_socket fd; unsigned int addr; fd_set wfds; struct timeval tv[1]; #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) int flags; #endif #ifdef FEATURE_ACL struct access_control_addr dst[1]; #endif /* def FEATURE_ACL */ /* Don't leak memory when retrying. */ freez(csp->http->host_ip_addr_str); memset((char *)&inaddr, 0, sizeof inaddr); if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE) { csp->http->host_ip_addr_str = strdup("unknown"); return(JB_INVALID_SOCKET); } #ifdef FEATURE_ACL dst->addr = ntohl(addr); dst->port = portnum; if (block_acl(dst, csp)) { #ifdef __OS2__ errno = SOCEPERM; #else errno = EPERM; #endif return(JB_INVALID_SOCKET); } #endif /* def FEATURE_ACL */ inaddr.sin_addr.s_addr = addr; inaddr.sin_family = AF_INET; csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr)); #ifndef _WIN32 if (sizeof(inaddr.sin_port) == sizeof(short)) #endif /* ndef _WIN32 */ { inaddr.sin_port = htons((unsigned short) portnum); } #ifndef _WIN32 else { inaddr.sin_port = htonl((unsigned long)portnum); } #endif /* ndef _WIN32 */ fd = socket(inaddr.sin_family, SOCK_STREAM, 0); #ifdef _WIN32 if (fd == JB_INVALID_SOCKET) #else if (fd < 0) #endif { return(JB_INVALID_SOCKET); } #ifndef _WIN32 if (fd >= FD_SETSIZE) { log_error(LOG_LEVEL_ERROR, "Server socket number too high to use select(): %d >= %d", fd, FD_SETSIZE); close_socket(fd); return JB_INVALID_SOCKET; } #endif set_no_delay_flag(fd); int yes = 1; setsockopt (fd, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof (yes)); #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) if ((flags = fcntl(fd, F_GETFL, 0)) != -1) { flags |= O_NDELAY; fcntl(fd, F_SETFL, flags); #ifdef FEATURE_EXTERNAL_FILTERS mark_socket_for_close_on_execute(fd); #endif } #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET) { #ifdef _WIN32 if (errno == WSAEINPROGRESS) #elif __OS2__ if (sock_errno() == EINPROGRESS) #else /* ifndef _WIN32 */ if (errno == EINPROGRESS) #endif /* ndef _WIN32 || __OS2__ */ { break; } #ifdef __OS2__ if (sock_errno() != EINTR) #else if (errno != EINTR) #endif /* __OS2__ */ { close_socket(fd); return(JB_INVALID_SOCKET); } } #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) if (flags != -1) { flags &= ~O_NDELAY; fcntl(fd, F_SETFL, flags); } #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ /* wait for connection to complete */ FD_ZERO(&wfds); FD_SET(fd, &wfds); tv->tv_sec = 30; tv->tv_usec = 0; /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */ if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0) { close_socket(fd); return(JB_INVALID_SOCKET); } return(fd); }
/********************************************************************* * * Function : acl_addr * * Description : Called from `load_config' to parse an ACL address. * * Parameters : * 1 : aspec = String specifying ACL address. * 2 : aca = struct access_control_addr to fill in. * * Returns : 0 => Ok, everything else is an error. * *********************************************************************/ int filters::acl_addr(const char *aspec, access_control_addr *aca) { int masklength; #ifdef HAVE_RFC2553 struct addrinfo hints, *result; uint8_t *mask_data; in_port_t *mask_port; unsigned int addr_len; #else long port; #endif /* def HAVE_RFC2553 */ char *p; char *acl_spec = NULL; #ifdef HAVE_RFC2553 /* XXX: Depend on ai_family */ masklength = 128; #else masklength = 32; port = 0; #endif /* * Use a temporary acl spec copy so we can log * the unmodified original in case of parse errors. */ acl_spec = strdup(aspec); if (acl_spec == NULL) { /* XXX: This will be logged as parse error. */ return(-1); } if ((p = strchr(acl_spec, '/')) != NULL) { *p++ = '\0'; if (ijb_isdigit(*p) == 0) { freez(acl_spec); acl_spec = NULL; return(-1); } masklength = atoi(p); } if ((masklength < 0) || #ifdef HAVE_RFC2553 (masklength > 128) #else (masklength > 32) #endif ) { freez(acl_spec); acl_spec = NULL; return(-1); } if ((*acl_spec == '[') && (NULL != (p = strchr(acl_spec, ']')))) { *p = '\0'; std::memmove(acl_spec, acl_spec + 1, (size_t)(p - acl_spec)); if (*++p != ':') { p = NULL; } } else { p = strchr(acl_spec, ':'); } #ifdef HAVE_RFC2553 std::memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; int i = getaddrinfo(acl_spec, ((p) ? ++p : NULL), &hints, &result); freez(acl_spec); acl_spec = NULL; if (i != 0) { errlog::log_error(LOG_LEVEL_ERROR, "Can not resolve [%s]:%s: %s", acl_spec, p, gai_strerror(i)); return(-1); } /* TODO: Allow multihomed hostnames */ std::memcpy(&(aca->_addr), result->ai_addr, result->ai_addrlen); freeaddrinfo(result); #else if (p != NULL) { char *endptr; *p++ = '\0'; port = strtol(p, &endptr, 10); if (port <= 0 || port > 65535 || *endptr != '\0') { freez(acl_spec); acl_spec = NULL; return(-1); } } aca->_port = (unsigned long)port; aca->_addr = ntohl(resolve_hostname_to_ip(acl_spec)); freez(acl_spec); acl_spec = NULL; if (aca->_addr == INADDR_NONE) { /* XXX: This will be logged as parse error. */ return(-1); } #endif /* def HAVE_RFC2553 */ /* build the netmask */ #ifdef HAVE_RFC2553 /* Clip masklength according to current family. */ if ((aca->_addr.ss_family == AF_INET) && (masklength > 32)) { masklength = 32; } aca->_mask.ss_family = aca->_addr.ss_family; if (sockaddr_storage_to_ip(&aca->_mask, &mask_data, &addr_len, &mask_port)) { return(-1); } if (p) { /* ACL contains a port number, check ports in the future. */ *mask_port = 1; } else ((struct sockaddr_in*)&aca->_mask)->sin_port = 0; /* * XXX: This could be optimized to operate on whole words instead * of octets (128-bit CPU could do it in one iteration). */ /* * Octets after prefix can be ommitted because of * previous initialization to zeros. */ for (size_t i = 0; (i < addr_len) && masklength; i++) { if (masklength >= 8) { mask_data[i] = 0xFF; masklength -= 8; } else { /* * XXX: This assumes MSB of octet is on the left side. * This should be true for all architectures or solved * by the link layer. */ mask_data[i] = (uint8_t)~((1 << (8 - masklength)) - 1); masklength = 0; } } #else aca->mask = 0; for (i=1; i <= masklength ; i++) { aca->mask |= (1U << (32 - i)); } /* now mask off the host portion of the ip address * (i.e. save on the network portion of the address). */ aca->addr = aca->addr & aca->mask; #endif /* def HAVE_RFC2553 */ return(0); }
/********************************************************************* * * Function : bind_port * * Description : Call socket, set socket options, and listen. * Called by listen_loop to "boot up" our proxy address. * * Parameters : * 1 : hostnam = TCP/IP address to bind/listen to * 2 : portnum = port to listen on * 3 : pfd = pointer used to return file descriptor. * * Returns : if success, returns 0 and sets *pfd. * if failure, returns -3 if address is in use, * -2 if address unresolvable, * -1 otherwise *********************************************************************/ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) { struct sockaddr_in inaddr; jb_socket fd; #ifndef _WIN32 int one = 1; #endif /* ndef _WIN32 */ *pfd = JB_INVALID_SOCKET; memset((char *)&inaddr, '\0', sizeof inaddr); inaddr.sin_family = AF_INET; inaddr.sin_addr.s_addr = resolve_hostname_to_ip(hostnam); if (inaddr.sin_addr.s_addr == INADDR_NONE) { return(-2); } #ifndef _WIN32 if (sizeof(inaddr.sin_port) == sizeof(short)) #endif /* ndef _WIN32 */ { inaddr.sin_port = htons((unsigned short) portnum); } #ifndef _WIN32 else { inaddr.sin_port = htonl((unsigned long) portnum); } #endif /* ndef _WIN32 */ fd = socket(AF_INET, SOCK_STREAM, 0); #ifdef _WIN32 if (fd == JB_INVALID_SOCKET) #else if (fd < 0) #endif { return(-1); } #ifndef _WIN32 /* * This is not needed for Win32 - in fact, it stops * duplicate instances of Privoxy from being caught. * * On UNIX, we assume the user is sensible enough not * to start Junkbuster multiple times on the same IP. * Without this, stopping and restarting Privoxy * from a script fails. * Note: SO_REUSEADDR is meant to only take over * sockets which are *not* in listen state in Linux, * e.g. sockets in TIME_WAIT. YMMV. */ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); #endif /* ndef _WIN32 */ if (bind(fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0) { #ifdef _WIN32 errno = WSAGetLastError(); if (errno == WSAEADDRINUSE) #else if (errno == EADDRINUSE) #endif { close_socket(fd); return(-3); } else { close_socket(fd); return(-1); } } while (listen(fd, 5) == -1) { if (errno != EINTR) { return(-1); } } *pfd = fd; return 0; }
/********************************************************************* * * Function : connect_to * * Description : Open a socket and connect to it. Will check * that this is allowed according to ACL. * * Parameters : * 1 : host = hostname to connect to * 2 : portnum = port to connent on * 3 : csp = Current client state (buffers, headers, etc...) * Not modified, only used for source IP and ACL. * * Returns : JB_INVALID_SOCKET => failure, else it is the socket * file descriptor. * *********************************************************************/ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) { #define MAX_EVENTS 1024 struct sockaddr_in inaddr; jb_socket fd, epollfd; int addr; //fd_set wfds; //struct timeval tv[1]; struct epoll_event ev; struct epoll_event evlist[MAX_EVENTS]; #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) int flags; #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */ #ifdef FEATURE_ACL struct access_control_addr dst[1]; #endif /* def FEATURE_ACL */ memset((char *)&inaddr, 0, sizeof inaddr); if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE) { csp->http->host_ip_addr_str = strdup("unknown"); return(JB_INVALID_SOCKET); } log_error(LOG_LEVEL_GPC, "host == %s, port == %d", host, portnum); #ifdef FEATURE_ACL dst->addr = ntohl((unsigned long) addr); dst->port = portnum; if (block_acl(dst, csp)) { #ifdef __OS2__ errno = SOCEPERM; #else errno = EPERM; #endif return(JB_INVALID_SOCKET); } #endif /* def FEATURE_ACL */ inaddr.sin_addr.s_addr = addr; inaddr.sin_family = AF_INET; csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr)); #ifndef _WIN32 if (sizeof(inaddr.sin_port) == sizeof(short)) #endif /* ndef _WIN32 */ { inaddr.sin_port = htons((unsigned short) portnum); } #ifndef _WIN32 else { inaddr.sin_port = htonl((unsigned long)portnum); } #endif /* ndef _WIN32 */ #ifdef _WIN32 if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) == JB_INVALID_SOCKET) #else if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) < 0) #endif { return(JB_INVALID_SOCKET); } #ifdef TCP_NODELAY { /* turn off TCP coalescence */ int mi = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int)); } #endif /* def TCP_NODELAY */ #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) if ((flags = fcntl(fd, F_GETFL, 0)) != -1) { flags |= O_NDELAY; fcntl(fd, F_SETFL, flags); } #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET) { #ifdef _WIN32 if (errno == WSAEINPROGRESS) #elif __OS2__ if (sock_errno() == EINPROGRESS) #else /* ifndef _WIN32 */ if (errno == EINPROGRESS) #endif /* ndef _WIN32 || __OS2__ */ { break; } #ifdef __OS2__ if (sock_errno() != EINTR) #else if (errno != EINTR) #endif /* __OS2__ */ { close_socket(fd); return(JB_INVALID_SOCKET); } } #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) if (flags != -1) { flags &= ~O_NDELAY; fcntl(fd, F_SETFL, flags); } #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ /* wait for connection to complete */ //FD_ZERO(&wfds); //FD_SET(fd, &wfds); //tv->tv_sec = 30; //tv->tv_usec = 0; epollfd = epoll_create(MAX_EVENTS); if (epollfd == -1) { log_error(LOG_LEVEL_GPC, "epoll_create error"); return JB_INVALID_SOCKET; } ev.data.fd = fd; ev.events = EPOLLIN | EPOLLOUT; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { log_error(LOG_LEVEL_GPC, "epoll_ctl error"); return JB_INVALID_SOCKET; } log_error(LOG_LEVEL_GPC, "int connect() : before select"); /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */ #if 0 if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0) #endif if (epoll_wait(epollfd, evlist, MAX_EVENTS, -1) <= 0) { log_error(LOG_LEVEL_GPC, "in select"); close_socket(fd); return(JB_INVALID_SOCKET); } log_error(LOG_LEVEL_GPC, "int connect() : after select"); return(fd); }