static void local_bind(CLI * c) { if (!c->bind_addr) return; if (ntohs(c->bind_addr->in.sin_port) >= 1024) { if (!bind(c->fd, &c->bind_addr->sa, addr_len(c->bind_addr))) { s_log(LOG_INFO, "local_bind succeeded on the original port"); return; } if (get_last_socket_error() != S_EADDRINUSE) { sockerror("local_bind (original port)"); longjmp(c->err, 1); } } c->bind_addr->in.sin_port = htons(0); if (!bind(c->fd, &c->bind_addr->sa, addr_len(c->bind_addr))) { s_log(LOG_INFO, "local_bind succeeded on an ephemeral port"); return; } sockerror("local_bind (ephemeral port)"); longjmp(c->err, 1); }
static int connect_remote(CLI *c) { /* connect to remote host */ SOCKADDR_UNION bind_addr, addr; SOCKADDR_LIST resolved_list, *address_list; int error; int s; /* destination socket */ u16 i; /* setup address_list */ if(c->opt->option.delayed_lookup) { resolved_list.num=0; if(!name2addrlist(&resolved_list, c->opt->remote_address, DEFAULT_LOOPBACK)) return -1; /* no host resolved */ address_list=&resolved_list; } else /* use pre-resolved addresses */ address_list=&c->opt->remote_addr; /* try to connect each host from the list */ for(i=0; i<address_list->num; i++) { memcpy(&addr, address_list->addr + address_list->cur, sizeof(SOCKADDR_UNION)); address_list->cur=(address_list->cur+1)%address_list->num; /* race condition is possible, but harmless in this case */ if((s=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) { sockerror("remote socket"); return -1; } if(alloc_fd(s)) return -1; if(c->bind_addr.num) { /* explicit local bind or transparent proxy */ memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION)); if(bind(s, &bind_addr.sa, addr_len(bind_addr))<0) { sockerror("bind transparent"); closesocket(s); return -1; } } /* try to connect for the 1st time */ s_ntop(c->connecting_address, &addr); s_log(LOG_DEBUG, "%s connecting %s", c->opt->servname, c->connecting_address); if(!connect(s, &addr.sa, addr_len(addr))) return s; /* no error -> success (should not be possible) */ error=get_last_socket_error(); if(error!=EINPROGRESS && error!=EWOULDBLOCK) { s_log(LOG_ERR, "remote connect (%s): %s (%d)", c->connecting_address, my_strerror(error), error); closesocket(s); continue; /* next IP */ } if(!connect_wait(c, s, c->opt->timeout_connect)) return s; /* success! */ closesocket(s); /* error -> next IP */ } return -1; }
/* open new ports, update fds */ int bind_ports(void) { SERVICE_OPTIONS *opt; char *local_address; #ifdef USE_LIBWRAP /* execute after parse_commandline() to know service_options.next, * but as early as possible to avoid leaking file descriptors */ /* retry on each bind_ports() in case stunnel.conf was reloaded without "libwrap = no" */ libwrap_init(); #endif /* USE_LIBWRAP */ s_poll_init(fds); s_poll_add(fds, signal_pipe[0], 1, 0); /* allow clean unbind_ports() even though bind_ports() was not fully performed */ for(opt=service_options.next; opt; opt=opt->next) if(opt->option.accept) opt->fd=-1; for(opt=service_options.next; opt; opt=opt->next) { if(opt->option.accept) { opt->fd=s_socket(opt->local_addr.sa.sa_family, SOCK_STREAM, 0, 1, "accept socket"); if(opt->fd<0) return 1; if(set_socket_options(opt->fd, 0)<0) { closesocket(opt->fd); return 1; } /* local socket can't be unnamed */ local_address=s_ntop(&opt->local_addr, addr_len(&opt->local_addr)); if(bind(opt->fd, &opt->local_addr.sa, addr_len(&opt->local_addr))) { s_log(LOG_ERR, "Error binding service [%s] to %s", opt->servname, local_address); sockerror("bind"); closesocket(opt->fd); str_free(local_address); return 1; } if(listen(opt->fd, SOMAXCONN)) { sockerror("listen"); closesocket(opt->fd); str_free(local_address); return 1; } s_poll_add(fds, opt->fd, 1, 0); s_log(LOG_DEBUG, "Service [%s] (FD=%d) bound to %s", opt->servname, opt->fd, local_address); str_free(local_address); } else if(opt->option.program && opt->option.remote) { /* create exec+connect services */ create_client(-1, -1, alloc_client_session(opt, -1, -1), client_thread); } } return 0; /* OK */ }
/* connect remote host */ NOEXPORT int connect_remote(CLI *c) { int fd, ind_start, ind_try, ind_cur; setup_connect_addr(c); ind_start=c->connect_addr.cur; /* the race condition here can be safely ignored */ if(c->opt->failover==FAILOVER_RR) c->connect_addr.cur=(ind_start+1)%c->connect_addr.num; /* try to connect each host from the list */ for(ind_try=0; ind_try<c->connect_addr.num; ind_try++) { ind_cur=(ind_start+ind_try)%c->connect_addr.num; c->fd=s_socket(c->connect_addr.addr[ind_cur].sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if(c->fd<0) longjmp(c->err, 1); local_bind(c); /* explicit local bind or transparent proxy */ if(s_connect(c, &c->connect_addr.addr[ind_cur], addr_len(&c->connect_addr.addr[ind_cur]))) { closesocket(c->fd); c->fd=-1; continue; /* next IP */ } print_bound_address(c); fd=c->fd; c->fd=-1; return fd; /* success! */ } longjmp(c->err, 1); return -1; /* some C compilers require a return value */ }
void cmd_remote_handler( int rc, int sock ) { char* argv[32]; int argc; IP clientaddr; socklen_t addrlen_ret; socklen_t addrlen; char request[1500]; REPLY reply; addrlen_ret = sizeof(IP); rc = recvfrom( sock, request, sizeof(request) - 1, 0, (struct sockaddr*)&clientaddr, &addrlen_ret ); if( rc <= 0 ) { return; } else { request[rc] = '\0'; } /* Initialize reply and reserve room for return status */ r_init( &reply, false ); r_printf( &reply, "_" ); /* Split up the command line into an argument array */ cmd_to_args( request, &argc, &argv[0], N_ELEMS(argv) ); /* Execute command line */ rc = cmd_exec( &reply, argc, argv ); /* Insert return code */ reply.data[0] = (rc == 0) ? '0' : '1'; addrlen = addr_len( &clientaddr ); rc = sendto( sock, reply.data, reply.size, 0, (struct sockaddr *)&clientaddr, addrlen ); }
int addr_match (struct sockaddr_storage *addr, struct sockaddr_storage *network, size_t mask) { struct sockaddr_storage addr_masked = *addr; addr_mask(&addr_masked, mask); return memcmp(addr_get_raw(&addr_masked), addr_get_raw(network), addr_len(network)) == 0; }
NOEXPORT void proxy_server(CLI *c) { SOCKADDR_UNION addr; socklen_t addrlen; char src_host[IP_LEN], dst_host[IP_LEN]; char src_port[PORT_LEN], dst_port[PORT_LEN], *proto; int err; addrlen=sizeof addr; if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)) { sockerror("getpeername"); longjmp(c->err, 1); } err=getnameinfo(&addr.sa, addr_len(&addr), src_host, IP_LEN, src_port, PORT_LEN, NI_NUMERICHOST|NI_NUMERICSERV); if(err) { s_log(LOG_ERR, "getnameinfo: %s", s_gai_strerror(err)); longjmp(c->err, 1); } addrlen=sizeof addr; if(getsockname(c->local_rfd.fd, &addr.sa, &addrlen)) { sockerror("getsockname"); longjmp(c->err, 1); } err=getnameinfo(&addr.sa, addr_len(&addr), dst_host, IP_LEN, dst_port, PORT_LEN, NI_NUMERICHOST|NI_NUMERICSERV); if(err) { s_log(LOG_ERR, "getnameinfo: %s", s_gai_strerror(err)); longjmp(c->err, 1); } switch(addr.sa.sa_family) { case AF_INET: proto="TCP4"; break; #ifdef USE_IPv6 case AF_INET6: proto="TCP6"; break; #endif default: /* AF_UNIX */ proto="UNKNOWN"; } fd_printf(c, c->remote_fd.fd, "PROXY %s %s %s %s %s", proto, src_host, dst_host, src_port, dst_port); }
int mcast_send_packet( const char msg[], IP *src_addr, const char ifname[] ) { char addrbuf[FULL_ADDSTRLEN+1]; int sock; IP addr; /* Copy address to separate field and set port */ memcpy( &addr, src_addr, addr_len( (IP*) src_addr ) ); port_set( &addr, addr_port(&g_lpd_addr) ); /* For IPv6, only send from link local addresses */ if( addr.ss_family == AF_INET6) { unsigned char* a = &((IP6*) &addr)->sin6_addr.s6_addr[0]; if( !(a[0] == 0xFE && a[1] == 0x80) ) { return 1; } } if( (sock = socket( gconf->af, SOCK_DGRAM, IPPROTO_UDP )) < 0 ) { log_warn( "LPD: Cannot create send socket: %s", strerror( errno ) ); goto skip; } const int opt_on = 1; if( setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &opt_on, sizeof(opt_on) ) < 0 ) { log_warn( "LPD: Unable to set SO_REUSEADDR: %s", strerror( errno ) ); goto skip; } if( bind( sock, (struct sockaddr*) &addr, addr_len( &addr ) ) < 0 ) { log_warn( "LPD: Cannot bind send socket: %s", strerror( errno ) ); goto skip; } if( sendto( sock, msg, strlen( msg ), 0, (struct sockaddr*) &g_lpd_addr, addr_len( &g_lpd_addr ) ) < 0 ) { log_warn( "LPD: Cannot send message from '%s': %s", str_addr( &addr, addrbuf ), strerror( errno ) ); goto skip; } log_debug( "LPD: Send peer discovery packet from source address: %s", str_addr( src_addr, addrbuf ) ); skip: close(sock); return 0; }
int kad_ping( const IP* addr ) { int rc; dht_lock(); rc = dht_ping_node( (struct sockaddr *)addr, addr_len( addr ) ); dht_unlock(); return (rc < 0) ? -1 : 0; }
int net_bind( const char name[], const char addr[], const char port[], const char ifname[], int protocol, int af ) { char addrbuf[FULL_ADDSTRLEN+1]; const int opt_on = 1; int sock; socklen_t addrlen; IP sockaddr; if( addr_parse( &sockaddr, addr, port, af ) != 0 ) { log_err( "%s: Failed to parse IP address '%s' and port '%s'.", name, addr, port ); return -1; } if( (sock = net_socket( name, ifname, protocol, sockaddr.ss_family )) < 0 ) { return -1; } if( sockaddr.ss_family == AF_INET6 ) { if( setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt_on, sizeof(opt_on) ) < 0 ) { close( sock ); log_err( "%s: Failed to set socket option IPV6_V6ONLY: '%s' (%s)", name, strerror( errno ), str_addr( &sockaddr, addrbuf ) ); return -1; } } addrlen = addr_len( &sockaddr ); if( bind( sock, (struct sockaddr*) &sockaddr, addrlen ) < 0 ) { close( sock ); log_err( "%s: Failed to bind socket to address: '%s' (%s)", name, strerror( errno ), str_addr( &sockaddr, addrbuf ) ); return -1; } if( protocol == IPPROTO_TCP && listen( sock, 5 ) < 0 ) { close( sock ); log_err( "%s: Failed to listen on socket: '%s' (%s)", name, strerror( errno ), str_addr( &sockaddr, addrbuf ) ); return -1; } log_info( ifname ? "%s: Bind to %s, interface %s" : "%s: Bind to %s", name, str_addr( &sockaddr, addrbuf ), ifname ); return sock; }
static OCSP_RESPONSE *ocsp_get_response(CLI *c, OCSP_REQUEST *req) { BIO *bio=NULL; OCSP_REQ_CTX *req_ctx=NULL; OCSP_RESPONSE *resp=NULL; int err; /* connect specified OCSP server (responder) */ c->fd=s_socket(c->opt->ocsp_addr.sa.sa_family, SOCK_STREAM, 0, 1, "OCSP: socket (auth_user)"); if(c->fd<0) goto cleanup; if(connect_blocking(c, &c->opt->ocsp_addr, addr_len(&c->opt->ocsp_addr))) goto cleanup; bio=BIO_new_fd(c->fd, BIO_NOCLOSE); if(!bio) goto cleanup; s_log(LOG_DEBUG, "OCSP: server connected"); /* OCSP protocol communication loop */ req_ctx=OCSP_sendreq_new(bio, c->opt->ocsp_path, req, -1); if(!req_ctx) { sslerror("OCSP: OCSP_sendreq_new"); goto cleanup; } while(OCSP_sendreq_nbio(&resp, req_ctx)==-1) { s_poll_init(c->fds); s_poll_add(c->fds, c->fd, BIO_should_read(bio), BIO_should_write(bio)); err=s_poll_wait(c->fds, c->opt->timeout_busy, 0); if(err==-1) sockerror("OCSP: s_poll_wait"); if(err==0) s_log(LOG_INFO, "OCSP: s_poll_wait: TIMEOUTbusy exceeded"); if(err<=0) goto cleanup; } /* s_log(LOG_DEBUG, "OCSP: context state: 0x%x", *(int *)req_ctx); */ /* http://www.mail-archive.com/[email protected]/msg61691.html */ if(!resp) { if(ERR_peek_error()) sslerror("OCSP: OCSP_sendreq_nbio"); else /* OpenSSL error: OCSP_sendreq_nbio does not use OCSPerr */ s_log(LOG_ERR, "OCSP: OCSP_sendreq_nbio: OpenSSL internal error"); } cleanup: if(req_ctx) OCSP_REQ_CTX_free(req_ctx); if(bio) BIO_free_all(bio); if(c->fd>=0) { closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ } return resp; }
static int auth_user(CLI *c) { struct servent *s_ent; /* structure for getservbyname */ SOCKADDR_UNION ident; /* IDENT socket name */ int fd; /* IDENT socket descriptor */ char name[STRLEN]; int retval; int error; if(!c->opt->username) return 0; /* -u option not specified */ if((fd=socket(c->peer_addr.addr[0].sa.sa_family, SOCK_STREAM, 0))<0) { sockerror("socket (auth_user)"); return -1; } if(alloc_fd(fd)) return -1; memcpy(&ident, &c->peer_addr.addr[0], sizeof(SOCKADDR_UNION)); s_ent=getservbyname("auth", "tcp"); if(s_ent) { ident.in.sin_port=s_ent->s_port; } else { s_log(LOG_WARNING, "Unknown service 'auth': using default 113"); ident.in.sin_port=htons(113); } if(connect(fd, &ident.sa, addr_len(ident))) { error=get_last_socket_error(); if(error!=EINPROGRESS && error!=EWOULDBLOCK) { sockerror("ident connect (auth_user)"); closesocket(fd); return -1; } if(connect_wait(c, fd, c->opt->timeout_connect)) { /* error */ closesocket(fd); return -1; } } s_log(LOG_DEBUG, "IDENT server connected"); if(fdprintf(c, fd, "%u , %u", ntohs(c->peer_addr.addr[0].in.sin_port), ntohs(c->opt->local_addr.addr[0].in.sin_port))<0) { sockerror("fdprintf (auth_user)"); closesocket(fd); return -1; } if(fdscanf(c, fd, "%*[^:]: USERID :%*[^:]:%s", name)!=1) { s_log(LOG_ERR, "Incorrect data from IDENT server"); closesocket(fd); return -1; } closesocket(fd); retval=strcmp(name, c->opt->username) ? -1 : 0; safestring(name); s_log(LOG_INFO, "IDENT resolved remote user to %s", name); return retval; }
static void make_sockets(CLI *c, int fd[2]) { /* make a pair of connected sockets */ #ifdef INET_SOCKET_PAIR SOCKADDR_UNION addr; socklen_t addrlen; int s; /* temporary socket awaiting for connection */ s=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#1"); if(s<0) longjmp(c->err, 1); c->fd=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#2"); if(c->fd<0) longjmp(c->err, 1); addrlen=sizeof addr; memset(&addr, 0, addrlen); addr.in.sin_family=AF_INET; addr.in.sin_addr.s_addr=htonl(INADDR_LOOPBACK); addr.in.sin_port=htons(0); /* dynamic port allocation */ if(bind(s, &addr.sa, addrlen)) log_error(LOG_DEBUG, get_last_socket_error(), "bind#1"); if(bind(c->fd, &addr.sa, addrlen)) log_error(LOG_DEBUG, get_last_socket_error(), "bind#2"); if(listen(s, 1)) { closesocket(s); sockerror("listen"); longjmp(c->err, 1); } if(getsockname(s, &addr.sa, &addrlen)) { closesocket(s); sockerror("getsockname"); longjmp(c->err, 1); } if(connect_blocking(c, &addr, addr_len(addr))) { closesocket(s); longjmp(c->err, 1); } fd[0]=s_accept(s, &addr.sa, &addrlen, 1, "accept"); if(fd[0]<0) { closesocket(s); longjmp(c->err, 1); } fd[1]=c->fd; c->fd=-1; closesocket(s); /* don't care about the result */ #else if(s_socketpair(AF_UNIX, SOCK_STREAM, 0, fd, 1, "socketpair")) longjmp(c->err, 1); #endif }
char *s_ntop(char *text, SOCKADDR_UNION *addr) { char host[IPLEN-6], port[6]; if(getnameinfo(&addr->sa, addr_len(*addr), host, IPLEN-6, port, 6, NI_NUMERICHOST|NI_NUMERICSERV)) { sockerror("getnameinfo"); strcpy(text, "unresolvable IP"); return text; } strcpy(text, host); strcat(text, ":"); strcat(text, port); return text; }
static int connect_remote(CLI *c) { /* connect remote host */ SOCKADDR_UNION addr; SOCKADDR_LIST resolved_list, *address_list; int fd, ind_try, ind_cur; /* setup address_list */ if(c->opt->option.delayed_lookup) { resolved_list.num=0; if(!name2addrlist(&resolved_list, c->opt->remote_address, DEFAULT_LOOPBACK)) { s_log(LOG_ERR, "No host resolved"); longjmp(c->err, 1); } address_list=&resolved_list; } else /* use pre-resolved addresses */ address_list=&c->opt->remote_addr; /* try to connect each host from the list */ for(ind_try=0; ind_try<address_list->num; ind_try++) { if(c->opt->failover==FAILOVER_RR) { ind_cur=address_list->cur; /* the race condition here can be safely ignored */ address_list->cur=(ind_cur+1)%address_list->num; } else { /* FAILOVER_PRIO */ ind_cur=ind_try; /* ignore address_list->cur */ } memcpy(&addr, address_list->addr+ind_cur, sizeof addr); c->fd=s_socket(addr.sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if(c->fd<0) longjmp(c->err, 1); if(c->bind_addr.num) /* explicit local bind or transparent proxy */ local_bind(c); if(connect_blocking(c, &addr, addr_len(addr))) { closesocket(c->fd); c->fd=-1; continue; /* next IP */ } print_bound_address(c); fd=c->fd; c->fd=-1; return fd; /* success! */ } longjmp(c->err, 1); return -1; /* some C compilers require a return value */ }
int mcast_leave_group( int sock, IP *addr ) { #if defined(MCAST_JOIN_GROUP) && !defined(__APPLE__) struct group_req req; req.gr_interface = 0; memcpy( &req.gr_group, addr, addr_len( addr ) ); if( setsockopt( sock, IPPROTO_IP, MCAST_LEAVE_GROUP, &req, sizeof(req) ) < 0 ) { log_warn( "LPD: Failed to leave multicast group: %s", strerror( errno ) ); return -1; } return 0; #else switch( addr->ss_family ) { case AF_INET: { struct ip_mreq mreq; memcpy( &mreq.imr_multiaddr, &((IP4*) addr)->sin_addr, 4 ); mreq.imr_interface.s_addr = htonl( INADDR_ANY ); if( setsockopt( sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq) ) < 0 ) { log_warn( "LPD: Failed to leave IPv4 multicast group: %s", strerror( errno ) ); return -1; } return 0; } case AF_INET6: { struct ipv6_mreq mreq6; memcpy( &mreq6.ipv6mr_multiaddr, &((IP6 *) addr)->sin6_addr, 16 ); mreq6.ipv6mr_interface = 0; if( setsockopt( sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6) ) < 0 ) { log_warn( "LPD: Failed to leave IPv6 multicast group: %s", strerror( errno ) ); return -1; } return 0; } default: return -1; } #endif }
static int connect_transparent(CLI *c) { /* connect the original dst */ SOCKADDR_UNION addr; socklen_t addrlen=sizeof addr; int retval; if(getsockopt(c->local_rfd.fd, SOL_IP, SO_ORIGINAL_DST, &addr, &addrlen)) { sockerror("setsockopt SO_ORIGINAL_DST"); longjmp(c->err, 1); } c->fd=s_socket(addr.sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if(c->fd<0) longjmp(c->err, 1); if(c->bind_addr.num) /* explicit local bind or transparent proxy */ local_bind(c); if(connect_blocking(c, &addr, addr_len(addr))) longjmp(c->err, 1); /* socket closed on cleanup */ print_bound_address(c); retval=c->fd; c->fd=-1; return retval; /* success! */ }
static int connect_remote(CLI * c) { int fd, ind_try, ind_cur; SOCKADDR_LIST *remote_addr; remote_addr = dynamic_remote_addr(c); for (ind_try = 0; ind_try < remote_addr->num; ind_try++) { if (c->opt->failover == FAILOVER_RR) { ind_cur = remote_addr->cur; remote_addr->cur = (ind_cur + 1) % remote_addr->num; } else { ind_cur = ind_try; } c->fd = s_socket(remote_addr->addr[ind_cur].sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if (c->fd < 0) longjmp(c->err, 1); local_bind(c); if (connect_blocking(c, &remote_addr->addr[ind_cur], addr_len(&remote_addr->addr[ind_cur]))) { closesocket(c->fd); c->fd = -1; continue; } print_bound_address(c); fd = c->fd; c->fd = -1; return fd; } longjmp(c->err, 1); return -1; }
/* connect remote host */ static int connect_remote(CLI *c) { int fd, ind_try, ind_cur; SOCKADDR_LIST *remote_addr; /* list of connect_blocking() targets */ remote_addr=dynamic_remote_addr(c); /* try to connect each host from the list */ for(ind_try=0; ind_try<remote_addr->num; ind_try++) { if(c->opt->failover==FAILOVER_RR) { ind_cur=remote_addr->cur; /* the race condition here can be safely ignored */ remote_addr->cur=(ind_cur+1)%remote_addr->num; } else { /* FAILOVER_PRIO */ ind_cur=ind_try; /* ignore remote_addr->cur */ } c->fd=s_socket(remote_addr->addr[ind_cur].sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if(c->fd<0) longjmp(c->err, 1); local_bind(c); /* explicit local bind or transparent proxy */ if(connect_blocking(c, &remote_addr->addr[ind_cur], addr_len(&remote_addr->addr[ind_cur]))) { closesocket(c->fd); c->fd=-1; continue; /* next IP */ } print_bound_address(c); fd=c->fd; c->fd=-1; return fd; /* success! */ } longjmp(c->err, 1); return -1; /* some C compilers require a return value */ }
static int ocsp_check(CLI *c, X509_STORE_CTX *callback_ctx) { int error, retval=0; SOCKADDR_UNION addr; X509 *cert; X509 *issuer=NULL; OCSP_CERTID *certID; BIO *bio=NULL; OCSP_REQUEST *request=NULL; OCSP_RESPONSE *response=NULL; OCSP_BASICRESP *basicResponse=NULL; ASN1_GENERALIZEDTIME *revoked_at=NULL, *this_update=NULL, *next_update=NULL; int status, reason; /* connect specified OCSP server (responder) */ c->fd=s_socket(c->opt->ocsp_addr.addr[0].sa.sa_family, SOCK_STREAM, 0, 0, "OCSP: socket (auth_user)"); if(c->fd<0) return 0; /* reject connection */ memcpy(&addr, &c->opt->ocsp_addr.addr[0], sizeof addr); if(connect_blocking(c, &addr, addr_len(addr))) goto cleanup; s_log(LOG_DEBUG, "OCSP: server connected"); /* get current certificate ID */ cert=X509_STORE_CTX_get_current_cert(callback_ctx); /* get current cert */ if(X509_STORE_CTX_get1_issuer(&issuer, callback_ctx, cert)!=1) { sslerror("OCSP: X509_STORE_CTX_get1_issuer"); goto cleanup; } certID=OCSP_cert_to_id(0, cert, issuer); if(!certID) { sslerror("OCSP: OCSP_cert_to_id"); goto cleanup; } /* build request */ request=OCSP_REQUEST_new(); if(!request) { sslerror("OCSP: OCSP_REQUEST_new"); goto cleanup; } if(!OCSP_request_add0_id(request, certID)) { sslerror("OCSP: OCSP_request_add0_id"); goto cleanup; } OCSP_request_add1_nonce(request, 0, -1); /* send the request and get a response */ /* FIXME: this code won't work with ucontext threading */ /* (blocking sockets are used) */ bio=BIO_new_fd(c->fd, BIO_NOCLOSE); response=OCSP_sendreq_bio(bio, c->opt->ocsp_path, request); if(!response) { sslerror("OCSP: OCSP_sendreq_bio"); goto cleanup; } error=OCSP_response_status(response); if(error!=OCSP_RESPONSE_STATUS_SUCCESSFUL) { s_log(LOG_WARNING, "OCSP: Responder error: %d: %s", error, OCSP_response_status_str(error)); goto cleanup; } s_log(LOG_DEBUG, "OCSP: Response received"); /* verify the response */ basicResponse=OCSP_response_get1_basic(response); if(!basicResponse) { sslerror("OCSP: OCSP_response_get1_basic"); goto cleanup; } if(OCSP_check_nonce(request, basicResponse)<=0) { sslerror("OCSP: OCSP_check_nonce"); goto cleanup; } if(OCSP_basic_verify(basicResponse, NULL, c->opt->revocation_store, c->opt->ocsp_flags)<=0) { sslerror("OCSP: OCSP_basic_verify"); goto cleanup; } if(!OCSP_resp_find_status(basicResponse, certID, &status, &reason, &revoked_at, &this_update, &next_update)) { sslerror("OCSP: OCSP_resp_find_status"); goto cleanup; } s_log(LOG_NOTICE, "OCSP: Status: %d: %s", status, OCSP_cert_status_str(status)); log_time(LOG_INFO, "OCSP: This update", this_update); log_time(LOG_INFO, "OCSP: Next update", next_update); /* check if the response is valid for at least one minute */ if(!OCSP_check_validity(this_update, next_update, 60, -1)) { sslerror("OCSP: OCSP_check_validity"); goto cleanup; } if(status==V_OCSP_CERTSTATUS_REVOKED) { if(reason==-1) s_log(LOG_WARNING, "OCSP: Certificate revoked"); else s_log(LOG_WARNING, "OCSP: Certificate revoked: %d: %s", reason, OCSP_crl_reason_str(reason)); log_time(LOG_NOTICE, "OCSP: Revoked at", revoked_at); goto cleanup; } retval=1; /* accept connection */ cleanup: if(bio) BIO_free_all(bio); if(issuer) X509_free(issuer); if(request) OCSP_REQUEST_free(request); if(response) OCSP_RESPONSE_free(response); if(basicResponse) OCSP_BASICRESP_free(basicResponse); closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ return retval; }
NOEXPORT void cache_transfer(SSL_CTX *ctx, const u_char type, const long timeout, const u_char *key, const size_t key_len, const u_char *val, const size_t val_len, unsigned char **ret, size_t *ret_len) { char session_id_txt[2*SSL_MAX_SSL_SESSION_ID_LENGTH+1]; const char hex[16]="0123456789ABCDEF"; const char *type_description[]={"new", "get", "remove"}; unsigned i; SOCKET s; ssize_t len; struct timeval t; CACHE_PACKET *packet; SERVICE_OPTIONS *section; if(ret) /* set error as the default result if required */ *ret=NULL; /* log the request information */ for(i=0; i<key_len && i<SSL_MAX_SSL_SESSION_ID_LENGTH; ++i) { session_id_txt[2*i]=hex[key[i]>>4]; session_id_txt[2*i+1]=hex[key[i]&0x0f]; } session_id_txt[2*i]='\0'; s_log(LOG_INFO, "cache_transfer: request=%s, timeout=%ld, id=%s, length=%lu", type_description[type], timeout, session_id_txt, (long unsigned)val_len); /* allocate UDP packet buffer */ if(key_len>SSL_MAX_SSL_SESSION_ID_LENGTH) { s_log(LOG_ERR, "cache_transfer: session id too big (%lu bytes)", (unsigned long)key_len); return; } if(val_len>MAX_VAL_LEN) { s_log(LOG_ERR, "cache_transfer: encoded session too big (%lu bytes)", (unsigned long)key_len); return; } packet=str_alloc(sizeof(CACHE_PACKET)); /* setup packet */ packet->version=1; packet->type=type; packet->timeout=htons((u_short)(timeout<64800?timeout:64800));/* 18 hours */ memcpy(packet->key, key, key_len); memcpy(packet->val, val, val_len); /* create the socket */ s=s_socket(AF_INET, SOCK_DGRAM, 0, 0, "cache_transfer: socket"); if(s==INVALID_SOCKET) { str_free(packet); return; } /* retrieve pointer to the section structure of this ctx */ section=SSL_CTX_get_ex_data(ctx, index_opt); if(sendto(s, (void *)packet, #ifdef USE_WIN32 (int) #endif (sizeof(CACHE_PACKET)-MAX_VAL_LEN+val_len), 0, §ion->sessiond_addr.sa, addr_len(§ion->sessiond_addr))<0) { sockerror("cache_transfer: sendto"); closesocket(s); str_free(packet); return; } if(!ret || !ret_len) { /* no response is required */ closesocket(s); str_free(packet); return; } /* set recvfrom timeout to 200ms */ t.tv_sec=0; t.tv_usec=200; if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&t, sizeof t)<0) { sockerror("cache_transfer: setsockopt SO_RCVTIMEO"); closesocket(s); str_free(packet); return; } /* retrieve response */ len=recv(s, (void *)packet, sizeof(CACHE_PACKET), 0); closesocket(s); if(len<0) { if(get_last_socket_error()==S_EWOULDBLOCK || get_last_socket_error()==S_EAGAIN) s_log(LOG_INFO, "cache_transfer: recv timeout"); else sockerror("cache_transfer: recv"); str_free(packet); return; } /* parse results */ if(len<(int)sizeof(CACHE_PACKET)-MAX_VAL_LEN || /* too short */ packet->version!=1 || /* wrong version */ safe_memcmp(packet->key, key, key_len)) { /* wrong session id */ s_log(LOG_DEBUG, "cache_transfer: malformed packet received"); str_free(packet); return; } if(packet->type!=CACHE_RESP_OK) { s_log(LOG_INFO, "cache_transfer: session not found"); str_free(packet); return; } *ret_len=(size_t)len-(sizeof(CACHE_PACKET)-MAX_VAL_LEN); *ret=str_alloc(*ret_len); s_log(LOG_INFO, "cache_transfer: session found"); memcpy(*ret, packet->val, *ret_len); str_free(packet); }
static void auth_user(CLI * c, char *accepted_address) { struct servent *s_ent; SOCKADDR_UNION ident; char *line, *type, *system, *user; if (!c->opt->username) return; if (c->peer_addr.sa.sa_family == AF_UNIX) { s_log(LOG_INFO, "IDENT not supported on Unix sockets"); return; } c->fd = s_socket(c->peer_addr.sa.sa_family, SOCK_STREAM, 0, 1, "socket (auth_user)"); if (c->fd < 0) longjmp(c->err, 1); memcpy(&ident, &c->peer_addr, c->peer_addr_len); s_ent = getservbyname("auth", "tcp"); if (s_ent) { ident.in.sin_port = s_ent->s_port; } else { s_log(LOG_WARNING, "Unknown service 'auth': using default 113"); ident.in.sin_port = htons(113); } if (connect_blocking(c, &ident, addr_len(&ident))) longjmp(c->err, 1); s_log(LOG_DEBUG, "IDENT server connected"); fd_printf(c, c->fd, "%u , %u", ntohs(c->peer_addr.in.sin_port), ntohs(c->opt->local_addr.in.sin_port)); line = fd_getline(c, c->fd); closesocket(c->fd); c->fd = -1; type = strchr(line, ':'); if (!type) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *type++ = '\0'; system = strchr(type, ':'); if (!system) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *system++ = '\0'; if (strcmp(type, " USERID ")) { s_log(LOG_ERR, "Incorrect INETD response type"); str_free(line); longjmp(c->err, 1); } user = strchr(system, ':'); if (!user) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *user++ = '\0'; while (*user == ' ') ++user; if (strcmp(user, c->opt->username)) { safestring(user); s_log(LOG_WARNING, "Connection from %s REFUSED by IDENT (user %s)", accepted_address, user); str_free(line); longjmp(c->err, 1); } s_log(LOG_INFO, "IDENT authentication passed"); str_free(line); }
/* * Join/leave a multicast group (ba mulitcast address) on the given interface. * The interface may be null. */ int mcast_set_group( int sock, IP *mcast_addr, const char ifname[], int join ) { #if defined(MCAST_JOIN_GROUP) && !defined(__APPLE__) && !defined(__FreeBSD__) struct group_req req; int level, optname; if( ifname ) { if( (req.gr_interface = if_nametoindex( ifname )) == 0 ) { log_warn( "LPD: Cannot find interface '%s' for multicast: %s", ifname, strerror( errno ) ); return -1; } } else { /* Register to first interface */ req.gr_interface = 0; } memcpy( &req.gr_group, mcast_addr, addr_len( mcast_addr ) ); level= (mcast_addr->ss_family == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6; optname = (join == 0) ? MCAST_LEAVE_GROUP : MCAST_JOIN_GROUP; if( setsockopt( sock, level, optname, &req, sizeof(req) ) < 0 ) { log_warn( "LPD: Failed to %s multicast group on %s: %s", join ? "join" : "leave", ifname ? ifname : "<any>", strerror( errno ) ); return -1; } return 0; #else switch( mcast_addr->ss_family ) { case AF_INET: { struct ip_mreqn mreq; memcpy( &mreq.imr_multiaddr, &((IP4*) mcast_addr)->sin_addr, 4 ); if( ifname ) { mreq.imr_address.s_addr = htonl( INADDR_ANY ); if( (mreq.imr_ifindex = if_nametoindex( ifname )) == 0 ) { log_warn( "LPD: Cannot find interface '%s' for multicast: %s", ifname, strerror( errno ) ); return -1; } } else { mreq.imr_address.s_addr = htonl( INADDR_ANY ); mreq.imr_ifindex = 0; } int opt = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; if( setsockopt(sock, IPPROTO_IP, opt, &mreq, sizeof(mreq)) < 0 ) { log_warn( "LPD: Failed to %s IPv4 multicast group on %s: %s", join ? "join" : "leave", ifname ? ifname : "<any>", strerror( errno ) ); return -1; } return 0; } case AF_INET6: { struct ipv6_mreq mreq6; memcpy( &mreq6.ipv6mr_multiaddr, &((IP6*) mcast_addr)->sin6_addr, 16 ); if( ifname ) { if( (mreq6.ipv6mr_interface = if_nametoindex( ifname )) == 0 ) { log_warn( "LPD: Cannot find interface '%s' for multicast: %s", ifname, strerror( errno ) ); return -1; } } else { mreq6.ipv6mr_interface = 0; } int opt = join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP; if( setsockopt(sock, IPPROTO_IPV6, opt, &mreq6, sizeof(mreq6)) < 0 ) { log_warn( "LPD: Failed to %s IPv6 multicast group on %s: %s", join ? "join" : "leave", ifname ? ifname : "<any>", strerror( errno ) ); return -1; } return 0; } default: return -1; } #endif }
NOEXPORT OCSP_RESPONSE *ocsp_get_response(CLI *c, OCSP_REQUEST *req, char *url) { BIO *bio=NULL; OCSP_REQ_CTX *req_ctx=NULL; OCSP_RESPONSE *resp=NULL; int err; char *host=NULL, *port=NULL, *path=NULL; SOCKADDR_UNION addr; int ssl; /* parse the OCSP URL */ if(!OCSP_parse_url(url, &host, &port, &path, &ssl)) { s_log(LOG_ERR, "OCSP: Failed to parse the OCSP URL"); goto cleanup; } if(ssl) { s_log(LOG_ERR, "OCSP: SSL not supported for OCSP" " - additional stunnel service needs to be defined"); goto cleanup; } memset(&addr, 0, sizeof addr); addr.in.sin_family=AF_INET; if(!hostport2addr(&addr, host, port)) { s_log(LOG_ERR, "OCSP: Failed to resolve the OCSP server address"); goto cleanup; } /* connect specified OCSP server (responder) */ c->fd=s_socket(addr.sa.sa_family, SOCK_STREAM, 0, 1, "OCSP: socket"); if(c->fd<0) goto cleanup; if(s_connect(c, &addr, addr_len(&addr))) goto cleanup; bio=BIO_new_fd(c->fd, BIO_NOCLOSE); if(!bio) goto cleanup; s_log(LOG_DEBUG, "OCSP: response retrieved"); /* OCSP protocol communication loop */ req_ctx=OCSP_sendreq_new(bio, path, req, -1); if(!req_ctx) { sslerror("OCSP: OCSP_sendreq_new"); goto cleanup; } while(OCSP_sendreq_nbio(&resp, req_ctx)==-1) { s_poll_init(c->fds); s_poll_add(c->fds, c->fd, BIO_should_read(bio), BIO_should_write(bio)); err=s_poll_wait(c->fds, c->opt->timeout_busy, 0); if(err==-1) sockerror("OCSP: s_poll_wait"); if(err==0) s_log(LOG_INFO, "OCSP: s_poll_wait: TIMEOUTbusy exceeded"); if(err<=0) goto cleanup; } #if 0 s_log(LOG_DEBUG, "OCSP: context state: 0x%x", *(int *)req_ctx); #endif /* http://www.mail-archive.com/[email protected]/msg61691.html */ if(resp) { s_log(LOG_DEBUG, "OCSP: request completed"); } else { if(ERR_peek_error()) sslerror("OCSP: OCSP_sendreq_nbio"); else /* OpenSSL error: OCSP_sendreq_nbio does not use OCSPerr */ s_log(LOG_ERR, "OCSP: OCSP_sendreq_nbio: OpenSSL internal error"); } cleanup: if(req_ctx) OCSP_REQ_CTX_free(req_ctx); if(bio) BIO_free_all(bio); if(c->fd>=0) { closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ } if(host) OPENSSL_free(host); if(port) OPENSSL_free(port); if(path) OPENSSL_free(path); return resp; }
/* open new ports, update fds */ int bind_ports(void) { SERVICE_OPTIONS *opt; char *local_address; int listening_section; #ifdef USE_LIBWRAP /* execute after options_cmdline() to know service_options.next, * but as early as possible to avoid leaking file descriptors */ /* retry on each bind_ports() in case stunnel.conf was reloaded without "libwrap = no" */ libwrap_init(); #endif /* USE_LIBWRAP */ s_poll_init(fds); s_poll_add(fds, signal_pipe[0], 1, 0); /* allow clean unbind_ports() even though bind_ports() was not fully performed */ for(opt=service_options.next; opt; opt=opt->next) if(opt->option.accept) opt->fd=INVALID_SOCKET; listening_section=0; for(opt=service_options.next; opt; opt=opt->next) { if(opt->option.accept) { if(listening_section<systemd_fds) { opt->fd=(SOCKET)(listen_fds_start+listening_section); s_log(LOG_DEBUG, "Listening file descriptor received from systemd (FD=%d)", opt->fd); } else { opt->fd=s_socket(opt->local_addr.sa.sa_family, SOCK_STREAM, 0, 1, "accept socket"); if(opt->fd==INVALID_SOCKET) return 1; s_log(LOG_DEBUG, "Listening file descriptor created (FD=%d)", opt->fd); } if(set_socket_options(opt->fd, 0)<0) { closesocket(opt->fd); opt->fd=INVALID_SOCKET; return 1; } /* local socket can't be unnamed */ local_address=s_ntop(&opt->local_addr, addr_len(&opt->local_addr)); /* we don't bind or listen on a socket inherited from systemd */ if(listening_section>=systemd_fds) { if(bind(opt->fd, &opt->local_addr.sa, addr_len(&opt->local_addr))) { s_log(LOG_ERR, "Error binding service [%s] to %s", opt->servname, local_address); sockerror("bind"); closesocket(opt->fd); opt->fd=INVALID_SOCKET; str_free(local_address); return 1; } if(listen(opt->fd, SOMAXCONN)) { sockerror("listen"); closesocket(opt->fd); opt->fd=INVALID_SOCKET; str_free(local_address); return 1; } } s_poll_add(fds, opt->fd, 1, 0); s_log(LOG_DEBUG, "Service [%s] (FD=%d) bound to %s", opt->servname, opt->fd, local_address); str_free(local_address); ++listening_section; } else if(opt->exec_name && opt->connect_addr.names) { /* create exec+connect services */ /* FIXME: needs to be delayed on reload with opt->option.retry set */ create_client(INVALID_SOCKET, INVALID_SOCKET, alloc_client_session(opt, INVALID_SOCKET, INVALID_SOCKET), client_thread); } } if(listening_section<systemd_fds) { s_log(LOG_ERR, "Too many listening file descriptors received from systemd, got %d", systemd_fds); return 1; } return 0; /* OK */ }
static void daemon_loop(void) { SOCKADDR_UNION addr; s_poll_set fds; LOCAL_OPTIONS *opt; get_limits(); s_poll_zero(&fds); #ifndef USE_WIN32 s_poll_add(&fds, signal_pipe_init(), 1, 0); #endif if(!local_options.next) { s_log(LOG_ERR, "No connections defined in config file"); exit(1); } num_clients=0; /* bind local ports */ for(opt=local_options.next; opt; opt=opt->next) { if(!opt->option.accept) /* no need to bind this service */ continue; memcpy(&addr, &opt->local_addr.addr[0], sizeof(SOCKADDR_UNION)); if((opt->fd=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) { sockerror("local socket"); exit(1); } if(alloc_fd(opt->fd)) exit(1); if(set_socket_options(opt->fd, 0)<0) exit(1); s_ntop(opt->local_address, &addr); if(bind(opt->fd, &addr.sa, addr_len(addr))) { s_log(LOG_ERR, "Error binding %s to %s", opt->servname, opt->local_address); sockerror("bind"); exit(1); } s_log(LOG_DEBUG, "%s bound to %s", opt->servname, opt->local_address); if(listen(opt->fd, 5)) { sockerror("listen"); exit(1); } #ifdef FD_CLOEXEC fcntl(opt->fd, F_SETFD, FD_CLOEXEC); /* close socket in child execvp */ #endif s_poll_add(&fds, opt->fd, 1, 0); } #if !defined (USE_WIN32) && !defined (__vms) if(!(options.option.foreground)) daemonize(); drop_privileges(); create_pid(); #endif /* !defined USE_WIN32 && !defined (__vms) */ /* create exec+connect services */ for(opt=local_options.next; opt; opt=opt->next) { if(opt->option.accept) /* skip ordinary (accepting) services */ continue; enter_critical_section(CRIT_CLIENTS); /* for multi-cpu machines */ num_clients++; leave_critical_section(CRIT_CLIENTS); create_client(-1, -1, alloc_client_session(opt, -1, -1), client); } while(1) { if(s_poll_wait(&fds, -1)<0) /* non-critical error */ log_error(LOG_INFO, get_last_socket_error(), "daemon_loop: s_poll_wait"); else for(opt=local_options.next; opt; opt=opt->next) if(s_poll_canread(&fds, opt->fd)) accept_connection(opt); } s_log(LOG_ERR, "INTERNAL ERROR: End of infinite loop 8-)"); }
static void local_bind(CLI *c) { int on; on=1; if(!c->bind_addr) return; #if defined(USE_WIN32) /* do nothing */ #elif defined(__linux__) /* non-local bind on Linux */ if(c->opt->option.transparent_src) { if(setsockopt(c->fd, SOL_IP, IP_TRANSPARENT, &on, sizeof on)) { sockerror("setsockopt IP_TRANSPARENT"); if(setsockopt(c->fd, SOL_IP, IP_FREEBIND, &on, sizeof on)) sockerror("setsockopt IP_FREEBIND"); else s_log(LOG_INFO, "IP_FREEBIND socket option set"); } else s_log(LOG_INFO, "IP_TRANSPARENT socket option set"); /* ignore the error to retain Linux 2.2 compatibility */ /* the error will be handled by bind(), anyway */ } #elif defined(IP_BINDANY) && defined(IPV6_BINDANY) /* non-local bind on FreeBSD */ if(c->opt->option.transparent_src) { if(c->bind_addr->sa.sa_family==AF_INET) { /* IPv4 */ if(setsockopt(c->fd, IPPROTO_IP, IP_BINDANY, &on, sizeof on)) { sockerror("setsockopt IP_BINDANY"); longjmp(c->err, 1); } } else { /* IPv6 */ if(setsockopt(c->fd, IPPROTO_IPV6, IPV6_BINDANY, &on, sizeof on)) { sockerror("setsockopt IPV6_BINDANY"); longjmp(c->err, 1); } } } #else /* unsupported platform */ if(c->opt->option.transparent_src) { s_log(LOG_ERR, "Transparent proxy in remote mode is not supported" " on this platform"); longjmp(c->err, 1); } #endif if(ntohs(c->bind_addr->in.sin_port)>=1024) { /* security check */ /* this is currently only possible with transparent_src */ if(!bind(c->fd, &c->bind_addr->sa, addr_len(c->bind_addr))) { s_log(LOG_INFO, "local_bind succeeded on the original port"); return; /* success */ } if(get_last_socket_error()!=S_EADDRINUSE) { sockerror("local_bind (original port)"); longjmp(c->err, 1); } } c->bind_addr->in.sin_port=htons(0); /* retry with ephemeral port */ if(!bind(c->fd, &c->bind_addr->sa, addr_len(c->bind_addr))) { s_log(LOG_INFO, "local_bind succeeded on an ephemeral port"); return; /* success */ } sockerror("local_bind (ephemeral port)"); longjmp(c->err, 1); }
static void auth_user(CLI *c) { #ifndef _WIN32_WCE struct servent *s_ent; /* structure for getservbyname */ #endif SOCKADDR_UNION ident; /* IDENT socket name */ char *line, *type, *system, *user; if(!c->opt->username) return; /* -u option not specified */ c->fd=s_socket(c->peer_addr.addr[0].sa.sa_family, SOCK_STREAM, 0, 1, "socket (auth_user)"); if(c->fd<0) longjmp(c->err, 1); memcpy(&ident, &c->peer_addr.addr[0], sizeof ident); #ifndef _WIN32_WCE s_ent=getservbyname("auth", "tcp"); if(s_ent) { ident.in.sin_port=s_ent->s_port; } else #endif { s_log(LOG_WARNING, "Unknown service 'auth': using default 113"); ident.in.sin_port=htons(113); } if(connect_blocking(c, &ident, addr_len(ident))) longjmp(c->err, 1); s_log(LOG_DEBUG, "IDENT server connected"); fdprintf(c, c->fd, "%u , %u", ntohs(c->peer_addr.addr[0].in.sin_port), ntohs(c->opt->local_addr.addr[0].in.sin_port)); line=fdgetline(c, c->fd); closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ type=strchr(line, ':'); if(!type) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *type++='\0'; system=strchr(type, ':'); if(!system) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *system++='\0'; if(strcmp(type, " USERID ")) { s_log(LOG_ERR, "Incorrect INETD response type"); str_free(line); longjmp(c->err, 1); } user=strchr(system, ':'); if(!user) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *user++='\0'; while(*user==' ') /* skip leading spaces */ ++user; if(strcmp(user, c->opt->username)) { safestring(user); s_log(LOG_WARNING, "Connection from %s REFUSED by IDENT (user %s)", c->accepted_address, user); str_free(line); longjmp(c->err, 1); } s_log(LOG_INFO, "IDENT authentication passed"); str_free(line); }
static void local_bind(CLI *c) { SOCKADDR_UNION addr; int on; on=1; memcpy(&addr, &c->bind_addr.addr[0], sizeof addr); #if defined(USE_WIN32) /* do nothing */ #elif defined(IP_TRANSPARENT) /* non-local bind on Linux */ if(c->opt->option.transparent_src) { if(setsockopt(c->fd, SOL_IP, IP_TRANSPARENT, &on, sizeof on)) sockerror("setsockopt IP_TRANSPARENT"); /* ignore the error to retain Linux 2.2 compatibility */ /* the error will be handled by bind(), anyway */ } #elif defined(IP_BINDANY) && defined(IPV6_BINDANY) /* non-local bind on FreeBSD */ if(c->opt->option.transparent_src) { if(addr.sa.sa_family==AF_INET) { /* IPv4 */ if(setsockopt(c->fd, IPPROTO_IP, IP_BINDANY, &on, sizeof on)) { sockerror("setsockopt IP_BINDANY"); longjmp(c->err, 1); } } else { /* IPv6 */ if(setsockopt(c->fd, IPPROTO_IPV6, IPV6_BINDANY, &on, sizeof on)) { sockerror("setsockopt IPV6_BINDANY"); longjmp(c->err, 1); } } } #else /* unsupported platform */ if(c->opt->option.transparent_src) { s_log(LOG_ERR, "Transparent proxy in remote mode is not supported" " on this platform"); longjmp(c->err, 1); } #endif if(ntohs(addr.in.sin_port)>=1024) { /* security check */ if(!bind(c->fd, &addr.sa, addr_len(addr))) { s_log(LOG_INFO, "local_bind succeeded on the original port"); return; /* success */ } if(get_last_socket_error()!=EADDRINUSE #ifndef USE_WIN32 || !c->opt->option.transparent_src #endif /* USE_WIN32 */ ) { sockerror("local_bind (original port)"); longjmp(c->err, 1); } } addr.in.sin_port=htons(0); /* retry with ephemeral port */ if(!bind(c->fd, &addr.sa, addr_len(addr))) { s_log(LOG_INFO, "local_bind succeeded on an ephemeral port"); return; /* success */ } sockerror("local_bind (ephemeral port)"); longjmp(c->err, 1); }
int mcast_join_group( int sock, IP *addr, const char ifce[] ) { #if defined(MCAST_JOIN_GROUP) && !defined(__APPLE__) struct group_req req; if( ifce ) { if( (req.gr_interface = if_nametoindex( ifce )) == 0 ) { log_err( "LPD: Cannot find interface '%s' for multicast: %s", ifce, strerror( errno ) ); return -1; } } else { req.gr_interface = 0; } memcpy( &req.gr_group, addr, addr_len( addr ) ); if( setsockopt( sock, IPPROTO_IP, MCAST_JOIN_GROUP, &req, sizeof(req) ) < 0 ) { log_warn( "LPD: Failed to join multicast group: %s", strerror( errno ) ); return -1; } return 0; #else switch( addr->ss_family ) { case AF_INET: { struct ip_mreq mreq; struct ifreq ifreq; memcpy( &mreq.imr_multiaddr, &((IP4*) addr)->sin_addr, 4 ); if( ifce ) { strncpy( ifreq.ifr_name, ifce, IFNAMSIZ ); if( ioctl( sock, SIOCGIFADDR, &ifreq ) < 0 ) { log_err( "LPD: Cannot find interface '%s' for multicast: %s", ifce, strerror( errno ) ); return -1; } memcpy( &mreq.imr_interface, &((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr, 4); } else { mreq.imr_interface.s_addr = htonl( INADDR_ANY ); } if( setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0 ) { log_warn( "LPD: Failed to join IPv4 multicast group: %s", strerror( errno ) ); return -1; } return 0; } case AF_INET6: { struct ipv6_mreq mreq6; memcpy( &mreq6.ipv6mr_multiaddr, &((IP6*) addr)->sin6_addr, 16 ); if( ifce ) { if( (mreq6.ipv6mr_interface = if_nametoindex( ifce )) == 0 ) { log_err( "LPD: Cannot find interface '%s' for multicast: %s", ifce, strerror( errno ) ); return -1; } } else { mreq6.ipv6mr_interface = 0; } if( setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)) < 0 ) { log_warn( "LPD: Failed to join IPv6 multicast group: %s", strerror( errno ) ); return -1; } return 0; } default: return -1; } #endif }