* any messages from it.
* --Bleep Thomas Helvey <tomh@inxpress.net>
*/
+#include "config.h"
+
#include "s_auth.h"
#include "client.h"
#include "IPcheck.h"
#include "ircd.h"
#include "ircd_alloc.h"
#include "ircd_chattr.h"
+#include "ircd_events.h"
+#include "ircd_features.h"
#include "ircd_log.h"
#include "ircd_osdep.h"
+#include "ircd_snprintf.h"
#include "ircd_string.h"
#include "list.h"
#include "numeric.h"
#include "s_debug.h"
#include "s_misc.h"
#include "send.h"
-#include "sprintf_irc.h"
#include "struct.h"
#include "sys.h" /* TRUE bleah */
+#include <arpa/inet.h> /* inet_netof */
#include <netdb.h> /* struct hostent */
#include <string.h>
#include <stdlib.h>
} ReportType;
#define sendheader(c, r) \
- send((c)->fd, HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
+ send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
struct AuthRequest* AuthPollList = 0; /* GLOBAL - auth queries pending io */
static struct AuthRequest* AuthIncompleteList = 0;
+enum { AUTH_TIMEOUT = 60 };
+
+static void release_auth_client(struct Client* client);
+static void unlink_auth_request(struct AuthRequest* request,
+ struct AuthRequest** list);
+void free_auth_request(struct AuthRequest* auth);
+
+/*
+ * auth_timeout - timeout a given auth request
+ */
+static void auth_timeout_callback(struct Event* ev)
+{
+ struct AuthRequest* auth;
+
+ assert(0 != ev_timer(ev));
+ assert(0 != t_data(ev_timer(ev)));
+
+ auth = t_data(ev_timer(ev));
+
+ if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
+ auth->flags &= ~AM_TIMEOUT;
+
+ if (!(auth->flags & AM_FREE_MASK)) {
+ Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
+ ev_timer(ev)));
+ MyFree(auth); /* done with it, finally */
+ }
+ } else {
+ assert(ev_type(ev) == ET_EXPIRE);
+
+ destroy_auth_request(auth, 1);
+ }
+}
+
+/*
+ * auth_sock_callback - called when an event occurs on the socket
+ */
+static void auth_sock_callback(struct Event* ev)
+{
+ struct AuthRequest* auth;
+
+ assert(0 != ev_socket(ev));
+ assert(0 != s_data(ev_socket(ev)));
+
+ auth = s_data(ev_socket(ev));
+
+ switch (ev_type(ev)) {
+ case ET_DESTROY: /* being destroyed */
+ auth->flags &= ~AM_SOCKET;
+
+ if (!(auth->flags & AM_FREE_MASK)) {
+ Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
+ ev_socket(ev)));
+ MyFree(auth); /* done with it finally */
+ }
+ break;
+
+ case ET_CONNECT: /* socket connection completed */
+ Debug((DEBUG_LIST, "Connection completed for auth %p [%p]; sending query",
+ auth, ev_socket(ev)));
+ socket_state(&auth->socket, SS_CONNECTED);
+ send_auth_query(auth);
+ break;
+
+ case ET_READ: /* socket is readable */
+ case ET_EOF: /* end of file on socket */
+ case ET_ERROR: /* error on socket */
+ Debug((DEBUG_LIST, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
+ read_auth_reply(auth);
+ break;
+
+ default:
+#ifndef NDEBUG
+ abort(); /* unrecognized event */
+#endif
+ break;
+ }
+}
+
+/*
+ * destroy_auth_request - stop an auth request completely
+ */
+void destroy_auth_request(struct AuthRequest* auth, int send_reports)
+{
+ struct AuthRequest** authList;
+
+ if (IsDoingAuth(auth)) {
+ authList = &AuthPollList;
+ if (-1 < auth->fd) {
+ close(auth->fd);
+ auth->fd = -1;
+ socket_del(&auth->socket);
+ }
+
+ if (send_reports && IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_ID);
+ } else
+ authList = &AuthIncompleteList;
+
+ if (IsDNSPending(auth)) {
+ delete_resolver_queries(auth);
+ if (send_reports && IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_DNS);
+ }
+
+ if (send_reports)
+ log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
+ get_client_name(auth->client, HIDE_IP));
+
+ release_auth_client(auth->client);
+ unlink_auth_request(auth, authList);
+ free_auth_request(auth);
+}
+
/*
* make_auth_request - allocate a new auth request
*/
(struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
assert(0 != auth);
memset(auth, 0, sizeof(struct AuthRequest));
+ auth->flags = AM_TIMEOUT;
auth->fd = -1;
auth->client = client;
- auth->timeout = CurrentTime + CONNECTTIMEOUT;
+ cli_auth(client) = auth;
+ timer_add(&auth->timeout, auth_timeout_callback, (void*) auth, TT_RELATIVE,
+ AUTH_TIMEOUT);
return auth;
}
*/
void free_auth_request(struct AuthRequest* auth)
{
- if (-1 < auth->fd)
+ if (-1 < auth->fd) {
close(auth->fd);
- MyFree(auth);
+ Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
+ socket_del(&auth->socket);
+ }
+ Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
+ timer_del(&auth->timeout);
}
/*
static void release_auth_client(struct Client* client)
{
assert(0 != client);
- client->lasttime = client->since = CurrentTime;
- if (client->fd > HighestFd)
- HighestFd = client->fd;
- LocalClientArray[client->fd] = client;
+ cli_auth(client) = 0;
+ cli_lasttime(client) = cli_since(client) = CurrentTime;
+ if (cli_fd(client) > HighestFd)
+ HighestFd = cli_fd(client);
+ LocalClientArray[cli_fd(client)] = client;
add_client_to_list(client);
+ socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
- client->username, client->sockhost, client->sock_ip));
+ cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
}
static void auth_kill_client(struct AuthRequest* auth)
* the ip#(s) for the socket is listed for the host.
*/
for (i = 0; hp->h_addr_list[i]; ++i) {
- if (0 == memcmp(hp->h_addr_list[i], &auth->client->ip,
+ if (0 == memcmp(hp->h_addr_list[i], &(cli_ip(auth->client)),
sizeof(struct in_addr)))
break;
}
if (!hp->h_addr_list[i]) {
if (IsUserPort(auth->client))
sendheader(auth->client, REPORT_IP_MISMATCH);
- sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
- auth->client->sock_ip, hp->h_name,
- ircd_ntoa(hp->h_addr_list[0]));
-#if defined(KILL_IPMISMATCH)
- auth_kill_client(auth);
- return;
-#endif
+ sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
+ cli_sock_ip(auth->client), hp->h_name,
+ ircd_ntoa(hp->h_addr_list[0]));
+ if (feature_bool(FEAT_KILL_IPMISMATCH)) {
+ auth_kill_client(auth);
+ return;
+ }
}
else {
++reply->ref_count;
- auth->client->dns_reply = reply;
- ircd_strncpy(auth->client->sockhost, hp->h_name, HOSTLEN);
+ cli_dns_reply(auth->client) = reply;
+ ircd_strncpy(cli_sockhost(auth->client), hp->h_name, HOSTLEN);
if (IsUserPort(auth->client))
sendheader(auth->client, REPORT_FIN_DNS);
}
assert(0 != auth);
close(auth->fd);
auth->fd = -1;
+ socket_del(&auth->socket);
if (IsUserPort(auth->client))
sendheader(auth->client, REPORT_FAIL_ID);
struct sockaddr_in remote_addr;
struct sockaddr_in local_addr;
int fd;
+ IOResult result;
assert(0 != auth);
assert(0 != auth->client);
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
-#if 0
- report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
-#endif
++ServerStats->is_abad;
return 0;
}
if ((MAXCONNECTIONS - 10) < fd) {
-#if 0
- report_error(CONNLIMIT_ERROR_MSG,
- get_client_name(auth->client, HIDE_IP), errno);
-#endif
close(fd);
return 0;
}
if (!os_set_nonblocking(fd)) {
-#if 0
- report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
-#endif
close(fd);
return 0;
}
* and machines with multiple IP addresses are common now
*/
memset(&local_addr, 0, sizeof(struct sockaddr_in));
- os_get_sockname(auth->client->fd, &local_addr);
+ os_get_sockname(cli_fd(auth->client), &local_addr);
local_addr.sin_port = htons(0);
if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
-#if 0
- report_error(BIND_ERROR_MSG,
- get_client_name(auth->client, HIDE_IP), errno);
-#endif
close(fd);
return 0;
}
- remote_addr.sin_addr.s_addr = auth->client->ip.s_addr;
+ remote_addr.sin_addr.s_addr = (cli_ip(auth->client)).s_addr;
remote_addr.sin_port = htons(113);
remote_addr.sin_family = AF_INET;
- if (!os_connect_nonb(fd, &remote_addr)) {
+ if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
+ !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
+ result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
+ SOCK_EVENT_READABLE, fd)) {
ServerStats->is_abad++;
/*
* No error report from this...
return 0;
}
+ auth->flags |= AM_SOCKET;
auth->fd = fd;
SetAuthConnect(auth);
+ if (result == IO_SUCCESS)
+ send_auth_query(auth); /* this does a SetAuthPending(auth) for us */
+
return 1;
}
return token;
}
-#if 0
-/*
- * GetValidIdent - parse ident query reply from identd server
- *
- * Inputs - pointer to ident buf
- * Output - NULL if no valid ident found, otherwise pointer to name
- * Side effects -
- */
-static char* GetValidIdent(char *buf)
-{
- int remp = 0;
- int locp = 0;
- char* colon1Ptr;
- char* colon2Ptr;
- char* colon3Ptr;
- char* commaPtr;
- char* remotePortString;
-
- /* All this to get rid of a sscanf() fun. */
- remotePortString = buf;
-
- colon1Ptr = strchr(remotePortString,':');
- if(!colon1Ptr)
- return 0;
-
- *colon1Ptr = '\0';
- colon1Ptr++;
- colon2Ptr = strchr(colon1Ptr,':');
- if(!colon2Ptr)
- return 0;
-
- *colon2Ptr = '\0';
- colon2Ptr++;
- commaPtr = strchr(remotePortString, ',');
-
- if(!commaPtr)
- return 0;
-
- *commaPtr = '\0';
- commaPtr++;
-
- remp = atoi(remotePortString);
- if(!remp)
- return 0;
-
- locp = atoi(commaPtr);
- if(!locp)
- return 0;
-
- /* look for USERID bordered by first pair of colons */
- if(!strstr(colon1Ptr, "USERID"))
- return 0;
-
- colon3Ptr = strchr(colon2Ptr,':');
- if(!colon3Ptr)
- return 0;
-
- *colon3Ptr = '\0';
- colon3Ptr++;
- return(colon3Ptr);
-}
-#endif
-
/*
* start_auth - starts auth (identd) and dns queries for a client
*/
+enum { LOOPBACK = 127 };
+
void start_auth(struct Client* client)
{
- struct DNSQuery query;
struct AuthRequest* auth = 0;
assert(0 != client);
auth = make_auth_request(client);
assert(0 != auth);
- query.vptr = auth;
- query.callback = auth_dns_callback;
+ Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
- if (IsUserPort(auth->client))
- sendheader(client, REPORT_DO_DNS);
+ if (!feature_bool(FEAT_NODNS)) {
+ if (LOOPBACK == inet_netof(cli_ip(client)))
+ strcpy(cli_sockhost(client), cli_name(&me));
+ else {
+ struct DNSQuery query;
-#if !defined(NODNS)
- client->dns_reply = gethost_byaddr((const char*) &client->ip, &query);
- if (client->dns_reply) {
- ++client->dns_reply->ref_count;
- ircd_strncpy(client->sockhost, client->dns_reply->hp->h_name, HOSTLEN);
- if (IsUserPort(auth->client))
- sendheader(client, REPORT_FIN_DNSC);
+ query.vptr = auth;
+ query.callback = auth_dns_callback;
+
+ if (IsUserPort(auth->client))
+ sendheader(client, REPORT_DO_DNS);
+
+ cli_dns_reply(client) = gethost_byaddr((const char*) &(cli_ip(client)),
+ &query);
+
+ if (cli_dns_reply(client)) {
+ ++(cli_dns_reply(client))->ref_count;
+ ircd_strncpy(cli_sockhost(client), cli_dns_reply(client)->hp->h_name,
+ HOSTLEN);
+ if (IsUserPort(auth->client))
+ sendheader(client, REPORT_FIN_DNSC);
+ Debug((DEBUG_LIST, "DNS entry for %p was cached", auth->client));
+ } else
+ SetDNSPending(auth);
+ }
}
- else
- SetDNSPending(auth);
-#endif
- if (start_auth_query(auth))
+ if (start_auth_query(auth)) {
+ Debug((DEBUG_LIST, "identd query for %p initiated successfully",
+ auth->client));
link_auth_request(auth, &AuthPollList);
- else if (IsDNSPending(auth))
+ } else if (IsDNSPending(auth)) {
+ Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
+ "waiting on DNS", auth->client));
link_auth_request(auth, &AuthIncompleteList);
- else {
+ } else {
+ Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
+ "no DNS pending; releasing immediately", auth->client));
free_auth_request(auth);
release_auth_client(client);
}
}
-/*
- * timeout_auth_queries - timeout resolver and identd requests
- * allow clients through if requests failed
- */
-void timeout_auth_queries(time_t now)
-{
- struct AuthRequest* auth;
- struct AuthRequest* auth_next = 0;
-
- for (auth = AuthPollList; auth; auth = auth_next) {
- auth_next = auth->next;
- if (auth->timeout < CurrentTime) {
- if (-1 < auth->fd) {
- close(auth->fd);
- auth->fd = -1;
- }
-
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FAIL_ID);
- if (IsDNSPending(auth)) {
- delete_resolver_queries(auth);
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FAIL_DNS);
- }
- ircd_log(L_INFO, "DNS/AUTH timeout %s",
- get_client_name(auth->client, HIDE_IP));
-
- release_auth_client(auth->client);
- unlink_auth_request(auth, &AuthPollList);
- free_auth_request(auth);
- }
- }
- for (auth = AuthIncompleteList; auth; auth = auth_next) {
- auth_next = auth->next;
- if (auth->timeout < CurrentTime) {
- delete_resolver_queries(auth);
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FAIL_DNS);
- ircd_log(L_INFO, "DNS timeout %s", get_client_name(auth->client, HIDE_IP));
-
- release_auth_client(auth->client);
- unlink_auth_request(auth, &AuthIncompleteList);
- free_auth_request(auth);
- }
- }
-}
-
/*
* send_auth_query - send the ident server a query giving "theirport , ourport"
* The write is only attempted *once* so it is deemed to be a fail if the
assert(0 != auth);
assert(0 != auth->client);
- if (!os_get_sockname(auth->client->fd, &us) ||
- !os_get_peername(auth->client->fd, &them)) {
+ if (!os_get_sockname(cli_fd(auth->client), &us) ||
+ !os_get_peername(cli_fd(auth->client), &them)) {
auth_error(auth, 1);
return;
}
- sprintf_irc(authbuf, "%u , %u\r\n",
- (unsigned int) ntohs(them.sin_port),
- (unsigned int) ntohs(us.sin_port));
+ ircd_snprintf(0, authbuf, sizeof(authbuf), "%u , %u\r\n",
+ (unsigned int) ntohs(them.sin_port),
+ (unsigned int) ntohs(us.sin_port));
if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
ClearAuthConnect(auth);
assert(0 != auth);
assert(0 != auth->client);
+ assert(auth = cli_auth(auth->client));
if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
buf[len] = '\0';
+ Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf));
username = check_ident_reply(buf);
+ Debug((DEBUG_LIST, "Username: %s", username));
}
close(auth->fd);
auth->fd = -1;
+ Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
+ socket_del(&auth->socket);
ClearAuth(auth);
if (!EmptyString(username)) {
- ircd_strncpy(auth->client->username, username, USERLEN);
+ ircd_strncpy(cli_username(auth->client), username, USERLEN);
/*
* Not needed, struct is zeroed by memset
* auth->client->username[USERLEN] = '\0';
}
else {
++ServerStats->is_abad;
-#if 0
- strcpy(auth->client->username, "unknown");
-#endif
}
unlink_auth_request(auth, &AuthPollList);
free_auth_request(auth);
}
}
-
-