/* * comm_connect_dns_callback() - called at the completion of the DNS request * * The DNS request has completed, so if we've got an error, return it, * otherwise we initiate the connect() */ static void comm_connect_dns_callback(void *vptr, struct DNSReply *reply) { fde_t *F = vptr; if (reply == NULL) { MyFree(F->dns_query); F->dns_query = NULL; comm_connect_callback(F, COMM_ERR_DNS); return; } /* No error, set a 10 second timeout */ comm_settimeout(F, 30*1000, comm_connect_timeout, NULL); /* Copy over the DNS reply info so we can use it in the connect() */ /* * Note we don't fudge the refcount here, because we aren't keeping * the DNS record around, and the DNS cache is gone anyway.. * -- adrian */ memcpy(&F->connect.hostaddr, &reply->addr, reply->addr.ss_len); /* The cast is hacky, but safe - port offset is same on v4 and v6 */ ((struct sockaddr_in *) &F->connect.hostaddr)->sin_port = F->connect.hostaddr.ss_port; F->connect.hostaddr.ss_len = reply->addr.ss_len; /* Now, call the tryconnect() routine to try a connect() */ MyFree(F->dns_query); F->dns_query = NULL; comm_connect_tryconnect(F, NULL); }
/* * comm_connect_dns_callback() - called at the completion of the DNS request * * The DNS request has completed, so if we've got an error, return it, * otherwise we initiate the connect() */ static void comm_connect_dns_callback(void *vptr, struct DNSReply *reply) { fde_t *F = vptr; /* Free dns_query now to avoid double reslist free -- jilles */ MyFree(F->dns_query); F->dns_query = NULL; if(!reply) { comm_connect_callback(F->fd, COMM_ERR_DNS); return; } /* No error, set a 10 second timeout */ comm_settimeout(F->fd, 30 * 1000, comm_connect_timeout, NULL); /* Copy over the DNS reply info so we can use it in the connect() */ #ifdef IPV6 if(reply->addr.ss_family == AF_INET6) { struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&F->connect.hostaddr; memcpy(&in6->sin6_addr, &((struct sockaddr_in6 *)&reply->addr)->sin6_addr, sizeof(struct in6_addr)); } else #endif { struct sockaddr_in *in = (struct sockaddr_in *)&F->connect.hostaddr; in->sin_addr.s_addr = ((struct sockaddr_in *)&reply->addr)->sin_addr.s_addr; } /* Now, call the tryconnect() routine to try a connect() */ comm_connect_tryconnect(F->fd, NULL); }
/* * ssl_handshake - let OpenSSL initialize the protocol. Register for * read/write events if necessary. */ static void ssl_handshake(int fd, struct Client *client_p) { int ret = SSL_accept(client_p->localClient->fd.ssl); X509 *cert; if ((cert = SSL_get_peer_certificate(client_p->localClient->fd.ssl)) != NULL) { int res = SSL_get_verify_result(client_p->localClient->fd.ssl); if (res == X509_V_OK || res == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN || res == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE || res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) { /* The client sent a certificate which verified OK */ base16_encode(client_p->certfp, sizeof(client_p->certfp), (const char*)cert->sha1_hash, sizeof(cert->sha1_hash)); } else { ilog(L_WARN, "Client %s!%s@%s gave bad SSL client certificate: %d", client_p->name, client_p->username, client_p->host, res); } X509_free(cert); } if (ret <= 0) { if((CurrentTime - client_p->firsttime) > 30) { exit_client(client_p, client_p, "Timeout during SSL handshake"); return; } switch (SSL_get_error(client_p->localClient->fd.ssl, ret)) { case SSL_ERROR_WANT_WRITE: comm_setselect(&client_p->localClient->fd, COMM_SELECT_WRITE, (PF *) ssl_handshake, client_p, 30); return; case SSL_ERROR_WANT_READ: comm_setselect(&client_p->localClient->fd, COMM_SELECT_READ, (PF *) ssl_handshake, client_p, 30); return; default: exit_client(client_p, client_p, "Error during SSL handshake"); return; } } comm_settimeout(&client_p->localClient->fd, 0, NULL, NULL); execute_callback(auth_cb, client_p); }
/* * comm_connect_dns_callback() - called at the completion of the DNS request * * The DNS request has completed, so if we've got an error, return it, * otherwise we initiate the connect() */ static void comm_connect_dns_callback(void *vptr, adns_answer * reply) { fde_t *F = vptr; if(!reply) { comm_connect_callback(F->fd, COMM_ERR_DNS); return; } if(reply->status != adns_s_ok) { /* Yes, callback + return */ comm_connect_callback(F->fd, COMM_ERR_DNS); MyFree(reply); MyFree(F->dns_query); F->dns_query = NULL; return; } /* No error, set a 10 second timeout */ comm_settimeout(F->fd, 30 * 1000, comm_connect_timeout, NULL); /* Copy over the DNS reply info so we can use it in the connect() */ /* * Note we don't fudge the refcount here, because we aren't keeping * the DNS record around, and the DNS cache is gone anyway.. * -- adrian */ #ifdef IPV6 if(reply->rrs.addr->addr.sa.sa_family == AF_INET6) { struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&F->connect.hostaddr; memcpy(&in6->sin6_addr, &reply->rrs.addr->addr.inet6.sin6_addr, sizeof(struct in6_addr)); } else #endif { struct sockaddr_in *in = (struct sockaddr_in *)&F->connect.hostaddr; in->sin_addr.s_addr = reply->rrs.addr->addr.inet.sin_addr.s_addr; } /* Now, call the tryconnect() routine to try a connect() */ MyFree(reply); comm_connect_tryconnect(F->fd, NULL); }
/* * comm_connect_callback() - call the callback, and continue with life */ static void comm_connect_callback(int fd, int status) { CNCB *hdl; fde_t *F = &fd_table[fd]; /* This check is gross..but probably necessary */ if(F->connect.callback == NULL) return; /* Clear the connect flag + handler */ hdl = F->connect.callback; F->connect.callback = NULL; F->flags.called_connect = 0; /* Clear the timeout handler */ comm_settimeout(F->fd, 0, NULL, NULL); /* Call the handler */ hdl(F->fd, status, F->connect.data); }
/* * comm_connect_callback() - call the callback, and continue with life */ static void comm_connect_callback(fde_t *fd, int status) { CNCB *hdl; /* This check is gross..but probably necessary */ if (fd->connect.callback == NULL) return; /* Clear the connect flag + handler */ hdl = fd->connect.callback; fd->connect.callback = NULL; /* Clear the timeout handler */ comm_settimeout(fd, 0, NULL, NULL); /* Call the handler */ hdl(fd, status, fd->connect.data); }
/* * comm_checktimeouts() - check the socket timeouts * * All this routine does is call the given callback/cbdata, without closing * down the file descriptor. When close handlers have been implemented, * this will happen. */ void comm_checktimeouts(void *notused) { int fd; PF *hdl; void *data; fde_t *F; for (fd = 0; fd <= highest_fd; fd++) { F = &fd_table[fd]; if(!F->flags.open) continue; if(F->flags.closing) continue; /* check flush functions */ if(F->flush_handler && F->flush_timeout > 0 && F->flush_timeout < CurrentTime) { hdl = F->flush_handler; data = F->flush_data; comm_setflush(F->fd, 0, NULL, NULL); hdl(F->fd, data); } /* check timeouts */ if(F->timeout_handler && F->timeout > 0 && F->timeout < CurrentTime) { /* Call timeout handler */ hdl = F->timeout_handler; data = F->timeout_data; comm_settimeout(F->fd, 0, NULL, NULL); hdl(F->fd, data); } } }
/* * comm_checktimeouts() - check the socket timeouts * * All this routine does is call the given callback/cbdata, without closing * down the file descriptor. When close handlers have been implemented, * this will happen. */ void comm_checktimeouts(void *notused) { int i; fde_t *F; PF *hdl; void *data; for (i = 0; i < FD_HASH_SIZE; i++) for (F = fd_hash[i]; F != NULL; F = fd_next_in_loop) { assert(F->flags.open); fd_next_in_loop = F->hnext; /* check flush functions */ if (F->flush_handler && F->flush_timeout > 0 && F->flush_timeout < CurrentTime) { hdl = F->flush_handler; data = F->flush_data; comm_setflush(F, 0, NULL, NULL); hdl(F, data); } /* check timeouts */ if (F->timeout_handler && F->timeout > 0 && F->timeout < CurrentTime) { /* Call timeout handler */ hdl = F->timeout_handler; data = F->timeout_data; comm_settimeout(F, 0, NULL, NULL); hdl(F, data); } } }
/* * void comm_connect_tcp(int fd, const char *host, u_short port, * struct sockaddr *clocal, int socklen, * CNCB *callback, void *data, int aftype, int timeout) * Input: An fd to connect with, a host and port to connect to, * a local sockaddr to connect from + length(or NULL to use the * default), a callback, the data to pass into the callback, the * address family. * Output: None. * Side-effects: A non-blocking connection to the host is started, and * if necessary, set up for selection. The callback given * may be called now, or it may be called later. */ void comm_connect_tcp(int fd, const char *host, u_short port, struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int aftype, int timeout) { void *ipptr = NULL; fde_t *F; s_assert(fd >= 0); F = &fd_table[fd]; F->flags.called_connect = 1; s_assert(callback); F->connect.callback = callback; F->connect.data = data; memset(&F->connect.hostaddr, 0, sizeof(F->connect.hostaddr)); #ifdef IPV6 if(aftype == AF_INET6) { struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&F->connect.hostaddr; SET_SS_LEN(F->connect.hostaddr, sizeof(struct sockaddr_in6)); in6->sin6_port = htons(port); in6->sin6_family = AF_INET6; ipptr = &in6->sin6_addr; } else #endif { struct sockaddr_in *in = (struct sockaddr_in *)&F->connect.hostaddr; SET_SS_LEN(F->connect.hostaddr, sizeof(struct sockaddr_in)); in->sin_port = htons(port); in->sin_family = AF_INET; ipptr = &in->sin_addr; } /* Note that we're using a passed sockaddr here. This is because * generally you'll be bind()ing to a sockaddr grabbed from * getsockname(), so this makes things easier. * XXX If NULL is passed as local, we should later on bind() to the * virtual host IP, for completeness. * -- adrian */ if((clocal != NULL) && (bind(F->fd, clocal, socklen) < 0)) { /* Failure, call the callback with COMM_ERR_BIND */ comm_connect_callback(F->fd, COMM_ERR_BIND); /* ... and quit */ return; } /* Next, if we have been given an IP, get the addr and skip the * DNS check (and head direct to comm_connect_tryconnect(). */ if(inetpton(aftype, host, ipptr) <= 0) { /* Send the DNS request, for the next level */ F->dns_query = MyMalloc(sizeof(struct DNSQuery)); F->dns_query->ptr = F; F->dns_query->callback = comm_connect_dns_callback; adns_gethost(host, aftype, F->dns_query); } else { /* We have a valid IP, so we just call tryconnect */ /* Make sure we actually set the timeout here .. */ comm_settimeout(F->fd, timeout * 1000, comm_connect_timeout, NULL); comm_connect_tryconnect(F->fd, NULL); } }
/* * void comm_connect_tcp(int fd, const char *host, unsigned short port, * struct sockaddr *clocal, int socklen, * CNCB *callback, void *data, int aftype, int timeout) * Input: An fd to connect with, a host and port to connect to, * a local sockaddr to connect from + length(or NULL to use the * default), a callback, the data to pass into the callback, the * address family. * Output: None. * Side-effects: A non-blocking connection to the host is started, and * if necessary, set up for selection. The callback given * may be called now, or it may be called later. */ void comm_connect_tcp(fde_t *fd, const char *host, unsigned short port, struct sockaddr *clocal, int socklen, CNCB *callback, void *data, int aftype, int timeout) { struct addrinfo hints, *res; char portname[PORTNAMELEN+1]; assert(callback); fd->connect.callback = callback; fd->connect.data = data; fd->connect.hostaddr.ss.ss_family = aftype; fd->connect.hostaddr.ss_port = htons(port); /* Note that we're using a passed sockaddr here. This is because * generally you'll be bind()ing to a sockaddr grabbed from * getsockname(), so this makes things easier. * XXX If NULL is passed as local, we should later on bind() to the * virtual host IP, for completeness. * -- adrian */ if ((clocal != NULL) && (bind(fd->fd, clocal, socklen) < 0)) { /* Failure, call the callback with COMM_ERR_BIND */ comm_connect_callback(fd, COMM_ERR_BIND); /* ... and quit */ return; } /* Next, if we have been given an IP, get the addr and skip the * DNS check (and head direct to comm_connect_tryconnect(). */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; snprintf(portname, PORTNAMELEN, "%d", port); if (irc_getaddrinfo(host, portname, &hints, &res)) { /* Send the DNS request, for the next level */ fd->dns_query = MyMalloc(sizeof(struct DNSQuery)); fd->dns_query->ptr = fd; fd->dns_query->callback = comm_connect_dns_callback; if (aftype == AF_INET6) gethost_byname_type(host, fd->dns_query, T_AAAA); else gethost_byname_type(host, fd->dns_query, T_A); } else { /* We have a valid IP, so we just call tryconnect */ /* Make sure we actually set the timeout here .. */ assert(res != NULL); memcpy(&fd->connect.hostaddr, res->ai_addr, res->ai_addrlen); fd->connect.hostaddr.ss_len = res->ai_addrlen; fd->connect.hostaddr.ss.ss_family = res->ai_family; irc_freeaddrinfo(res); comm_settimeout(fd, timeout*1000, comm_connect_timeout, NULL); comm_connect_tryconnect(fd, NULL); } }