unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

ident_lookup.c (6120B)

      1 /* src/modules/ident_lookup.c - Ident lookups (RFC1413)
      2  * (C) Copyright 2019 Bram Matthys (Syzop) and the UnrealIRCd team
      3  * License: GPLv2 or later
      4  */
      5 #include "unrealircd.h"
      6 
      7 ModuleHeader MOD_HEADER
      8 = {
      9 	"ident_lookup",
     10 	"1.0",
     11 	"Ident lookups (RFC1413)",
     12 	"UnrealIRCd Team",
     13 	"unrealircd-6",
     14 };
     15 
     16 /* Forward declarations */
     17 static EVENT(check_ident_timeout);
     18 static int ident_lookup_connect(Client *client);
     19 static void ident_lookup_send(int fd, int revents, void *data);
     20 static void ident_lookup_receive(int fd, int revents, void *data);
     21 static char *ident_lookup_parse(Client *client, char *buf);
     22 
     23 MOD_INIT()
     24 {
     25 	MARK_AS_OFFICIAL_MODULE(modinfo);
     26 	ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1); /* needed? or not? */
     27 	EventAdd(NULL, "check_ident_timeout", check_ident_timeout, NULL, 1000, 0);
     28 	HookAdd(modinfo->handle, HOOKTYPE_IDENT_LOOKUP, 0, ident_lookup_connect);
     29 
     30 	return MOD_SUCCESS;
     31 }
     32 
     33 MOD_LOAD()
     34 {
     35 	return MOD_SUCCESS;
     36 }
     37 
     38 MOD_UNLOAD()
     39 {
     40 	return MOD_SUCCESS;
     41 }
     42 
     43 
     44 static void ident_lookup_failed(Client *client)
     45 {
     46 	ircstats.is_abad++;
     47 	if (client->local->authfd != -1)
     48 	{
     49 		fd_close(client->local->authfd);
     50 		--OpenFiles;
     51 		client->local->authfd = -1;
     52 	}
     53 	ClearIdentLookupSent(client);
     54 	ClearIdentLookup(client);
     55 	if (should_show_connect_info(client))
     56 		sendto_one(client, NULL, ":%s %s", me.name, REPORT_FAIL_ID);
     57 }
     58 
     59 static EVENT(check_ident_timeout)
     60 {
     61 	Client *client, *next;
     62 
     63 	list_for_each_entry_safe(client, next, &unknown_list, lclient_node)
     64 	{
     65 		if (IsIdentLookup(client))
     66 		{
     67 			if (IsIdentLookupSent(client))
     68 			{
     69 				/* set::ident::connect-timeout */
     70 				if ((TStime() - client->local->creationtime) > IDENT_CONNECT_TIMEOUT)
     71 					ident_lookup_failed(client);
     72 			} else
     73 			{
     74 				/* set::ident::read-timeout */
     75 				if ((TStime() - client->local->creationtime) > IDENT_READ_TIMEOUT)
     76 					ident_lookup_failed(client);
     77 			}
     78 		}
     79 	}
     80 }
     81 
     82 /** Start the ident lookup for this user */
     83 static int ident_lookup_connect(Client *client)
     84 {
     85 	char buf[BUFSIZE];
     86 
     87 	snprintf(buf, sizeof buf, "identd: %s", get_client_name(client, TRUE));
     88 	if ((client->local->authfd = fd_socket(IsIPV6(client) ? AF_INET6 : AF_INET, SOCK_STREAM, 0, buf)) == -1)
     89 	{
     90 		ident_lookup_failed(client);
     91 		return 0;
     92 	}
     93 	if (++OpenFiles >= maxclients+1)
     94 	{
     95 		unreal_log(ULOG_FATAL, "io", "IDENT_ERROR_MAXCLIENTS", client,
     96 		           "Cannot do ident connection for $client.details: All connections in use");
     97 		fd_close(client->local->authfd);
     98 		--OpenFiles;
     99 		client->local->authfd = -1;
    100 		return 0;
    101 	}
    102 
    103 	if (should_show_connect_info(client))
    104 		sendto_one(client, NULL, ":%s %s", me.name, REPORT_DO_ID);
    105 
    106 	set_sock_opts(client->local->authfd, client, IsIPV6(client));
    107 
    108 	/* Bind to the IP the user got in */
    109 	unreal_bind(client->local->authfd, client->local->listener->ip, 0, IsIPV6(client));
    110 
    111 	/* And connect... */
    112 	if (!unreal_connect(client->local->authfd, client->ip, 113, IsIPV6(client)))
    113 	{
    114 		ident_lookup_failed(client);
    115 		return 0;
    116 	}
    117 	SetIdentLookupSent(client);
    118 	SetIdentLookup(client);
    119 
    120 	fd_setselect(client->local->authfd, FD_SELECT_WRITE, ident_lookup_send, client);
    121 
    122 	return 0;
    123 }
    124 
    125 /** Send the request to the ident server */
    126 static void ident_lookup_send(int fd, int revents, void *data)
    127 {
    128 	char authbuf[32];
    129 	Client *client = data;
    130 
    131 	ircsnprintf(authbuf, sizeof(authbuf), "%d , %d\r\n",
    132 		client->local->port,
    133 		client->local->listener->port);
    134 
    135 	if (WRITE_SOCK(client->local->authfd, authbuf, strlen(authbuf)) != strlen(authbuf))
    136 	{
    137 		if (ERRNO == P_EAGAIN)
    138 			return; /* Not connected yet, try again later */
    139 		ident_lookup_failed(client);
    140 		return;
    141 	}
    142 	ClearIdentLookupSent(client);
    143 
    144 	fd_setselect(client->local->authfd, FD_SELECT_READ, ident_lookup_receive, client);
    145 	fd_setselect(client->local->authfd, FD_SELECT_WRITE, NULL, client);
    146 
    147 	return;
    148 }
    149 
    150 /** Receive the ident response */
    151 static void ident_lookup_receive(int fd, int revents, void *userdata)
    152 {
    153 	Client *client = userdata;
    154 	char *ident = NULL;
    155 	char buf[512];
    156 	int len;
    157 
    158 	len = READ_SOCK(client->local->authfd, buf, sizeof(buf)-1);
    159 
    160 	/* We received a response. We don't bother with fragmentation
    161 	 * since that is not going to happen for such a short string.
    162 	 * Other IRCd's think the same and this simplifies things a lot.
    163 	 */
    164 
    165 	/* Before we continue, we can already tear down the connection
    166 	 * and set the appropriate flags that we are finished.
    167 	 */
    168 	fd_close(client->local->authfd);
    169 	--OpenFiles;
    170 	client->local->authfd = -1;
    171 	client->local->identbufcnt = 0;
    172 	ClearIdentLookup(client);
    173 
    174 	if (should_show_connect_info(client))
    175 		sendto_one(client, NULL, ":%s %s", me.name, REPORT_FIN_ID);
    176 
    177 	if (len > 0)
    178 	{
    179 		buf[len] = '\0'; /* safe, due to the READ_SOCK() being on sizeof(buf)-1 */
    180 		ident = ident_lookup_parse(client, buf);
    181 	}
    182 	if (ident)
    183 	{
    184 		strlcpy(client->ident, ident, USERLEN + 1);
    185 		SetIdentSuccess(client);
    186 		ircstats.is_asuc++;
    187 	} else {
    188 		ircstats.is_abad++;
    189 	}
    190 	return;
    191 }
    192 
    193 static char *ident_lookup_parse(Client *client, char *buf)
    194 {
    195 	/* <port> , <port> : USERID : <OSTYPE>: <username>
    196 	 * Actually the only thing we care about is <username>
    197 	 */
    198 	int port1 = 0, port2 = 0;
    199 	char *ostype = NULL;
    200 	char *username = NULL;
    201 	char *p, *p2;
    202 
    203 	skip_whitespace(&buf);
    204 	p = strchr(buf, ',');
    205 	if (!p)
    206 		return NULL;
    207 	*p = '\0';
    208 	port1 = atoi(buf); /* port1 is set */
    209 
    210 	/*  <port> : USERID : <OSTYPE>: <username> */
    211 	buf = p + 1;
    212 	p = strchr(buf, ':');
    213 	if (!p)
    214 		return NULL;
    215 	*p = '\0';
    216 	port2 = atoi(buf); /* port2 is set */
    217 
    218 	/*  USERID : <OSTYPE>: <username> */
    219 	buf = p + 1;
    220 	skip_whitespace(&buf);
    221 	if (strncmp(buf, "USERID", 6))
    222 		return NULL;
    223 	buf += 6; /* skip over strlen("USERID") */
    224 	skip_whitespace(&buf);
    225 	if (*buf != ':')
    226 		return NULL;
    227 	buf++;
    228 	skip_whitespace(&buf);
    229 
    230 	/*  <OSTYPE>: <username> */
    231 	p = strchr(buf, ':');
    232 	if (!p)
    233 		return NULL;
    234 
    235 	/*  <username> */
    236 	buf = p+1;
    237 	skip_whitespace(&buf);
    238 
    239 	/* Username */
    240 	// A) Skip any ~ or ^ at the start
    241 	for (; *buf; buf++)
    242 		if (!strchr("~^", *buf) && (*buf > 32))
    243 			break;
    244 	// B) Stop at the end, IOTW stop at newline, space, etc.
    245 	for (p=buf; *p; p++)
    246 	{
    247 		if (strchr("\n\r@:", *p) || (*p <= 32))
    248 		{
    249 			*p = '\0';
    250 			break;
    251 		}
    252 	}
    253 	if (*buf == '\0')
    254 		return NULL;
    255 	return buf;
    256 }