1 /*
2 * ident.c -- part of ident.mod
3 */
4 /*
5 * Copyright (c) 2018 - 2019 Michael Ortmann MIT License
6 * Copyright (C) 2019 - 2021 Eggheads Development Team
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23 #define MODULE_NAME "ident"
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include "src/mod/module.h"
29 #include "server.mod/server.h"
30
31 #define IDENT_METHOD_OIDENT 0
32 #define IDENT_METHOD_BUILTIN 1
33 #define IDENT_SIZE 1000 /* rfc1413 */
34
35 static Function *global = NULL, *server_funcs = NULL;
36
37 static int ident_method = IDENT_METHOD_OIDENT;
38 static int ident_port = 113;
39
40 static tcl_ints identints[] = {
41 {"ident-method", &ident_method, 0},
42 {"ident-port", &ident_port, 0},
43 {NULL, NULL, 0}
44 };
45
46 static void ident_builtin_off();
47
48 static cmd_t ident_raw[] = {
49 {"001", "", (IntFunc) ident_builtin_off, "ident:001"},
50 {NULL, NULL, NULL, NULL}
51 };
52
ident_activity(int idx,char * buf,int len)53 static void ident_activity(int idx, char *buf, int len)
54 {
55 int s;
56 char buf2[IDENT_SIZE + sizeof " : USERID : UNIX : \r\n" + NICKLEN], *pos;
57 size_t count;
58 ssize_t i;
59
60 s = answer(dcc[idx].sock, &dcc[idx].sockname, 0, 0);
61 killsock(dcc[idx].sock);
62 dcc[idx].sock = s;
63 if ((i = read(s, buf2, IDENT_SIZE)) < 0) {
64 putlog(LOG_MISC, "*", "Ident error: %s", strerror(errno));
65 return;
66 }
67 buf2[i - 1] = 0;
68 if (!(pos = strpbrk(buf2, "\r\n"))) {
69 putlog(LOG_MISC, "*", "Ident error: could not read request.");
70 return;
71 }
72 snprintf(pos, (sizeof buf2) - (pos - buf2), " : USERID : UNIX : %s\r\n", botname);
73 count = strlen(buf2) + 1;
74 if ((i = write(s, buf2, count)) != count) {
75 if (i < 0)
76 putlog(LOG_MISC, "*", "Ident error: %s", strerror(errno));
77 else
78 putlog(LOG_MISC, "*", "Ident error: Wrote %i bytes instead of %i bytes.", i, count);
79 return;
80 }
81 putlog(LOG_MISC, "*", "Ident: Responded.");
82 ident_builtin_off();
83 }
84
ident_display(int idx,char * buf)85 static void ident_display(int idx, char *buf)
86 {
87 strcpy(buf, "ident (ready)");
88 }
89
90 static struct dcc_table DCC_IDENTD = {
91 "IDENTD",
92 DCT_LISTEN,
93 NULL,
94 ident_activity,
95 NULL,
96 NULL,
97 ident_display,
98 NULL,
99 NULL,
100 NULL
101 };
102
ident_oidentd()103 static void ident_oidentd()
104 {
105 char *home = getenv("HOME");
106 FILE *fd;
107 long filesize;
108 char *data = NULL;
109 char path[121], line[256], buf[256], identstr[256];
110 #ifdef IPV6
111 char s[INET6_ADDRSTRLEN];
112 #else
113 char s[INET_ADDRSTRLEN];
114 #endif
115 int ret, prevtime, servidx, i;
116 socklen_t namelen;
117 struct sockaddr_storage ss;
118
119 snprintf(identstr, sizeof identstr, "### eggdrop_%s", pid_file);
120
121 if (!home) {
122 putlog(LOG_MISC, "*",
123 "Ident error: variable HOME is not in the current environment.");
124 return;
125 }
126 if (snprintf(path, sizeof path, "%s/.oidentd.conf", home) >= sizeof path) {
127 putlog(LOG_MISC, "*", "Ident error: path too long.");
128 return;
129 }
130 fd = fopen(path, "r");
131 if (fd != NULL) {
132 /* Calculate file size for buffer */
133 if (fseek(fd, 0, SEEK_END) == 0) {
134 filesize = ftell(fd);
135 if (filesize == -1) {
136 putlog(LOG_MISC, "*", "IDENT: Error reading oident.conf");
137 }
138 data = nmalloc(filesize + 256); /* Room for Eggdrop adds */
139 data[0] = '\0';
140
141 /* Read the file into buffer */
142 if (fseek(fd, 0, SEEK_SET) != 0) {
143 putlog(LOG_MISC, "*", "IDENT: Error setting oident.conf file pointer");
144 } else {
145 while (fgets(line, 255, fd)) {
146 /* If it is not an Eggdrop entry, don't mess with it */
147 if (!strstr(line, "### eggdrop_")) {
148 strncat(data, line, ((filesize + 256) - strlen(data)));
149 } else {
150 /* If it is Eggdrop but not me, check for expiration and remove */
151 if (!strstr(line, identstr)) {
152 strlcpy(buf, line, sizeof buf);
153 strtok(buf, "!");
154 prevtime = atoi(strtok(NULL, "!"));
155 if ((now - prevtime) > 300) {
156 putlog(LOG_DEBUG, "*", "IDENT: Removing expired oident.conf "
157 "entry: \"%s\"", buf);
158 } else {
159 strncat(data, line, ((filesize + 256) - strlen(data)));
160 }
161 }
162 }
163 }
164 }
165 }
166 fclose(fd);
167 } else {
168 putlog(LOG_MISC, "*", "IDENT: oident.conf missing, or error opening "
169 "for reading");
170 }
171 /* To minimize a known race condition, this code is called now */
172 servidx = -1;
173 for (i = 0; i < dcc_total; i++)
174 if (dcc[i].status & STAT_SERV) {
175 servidx = i;
176 break;
177 }
178 if (servidx < 0 ) {
179 putlog(LOG_MISC, "*", "IDENT: Error could not find server socket");
180 if (data)
181 nfree(data);
182 return;
183 }
184 namelen = sizeof ss;
185 ret = getsockname(dcc[servidx].sock, (struct sockaddr *) &ss, &namelen);
186 if (ret) {
187 putlog(LOG_DEBUG, "*", "IDENT: Error getting socket info for writing");
188 }
189 fd = fopen(path, "w");
190 if (fd != NULL) {
191 if (data) {
192 fprintf(fd, "%s", data);
193 }
194 if (ss.ss_family == AF_INET) {
195 struct sockaddr_in *saddr = (struct sockaddr_in *)&ss;
196 fprintf(fd, "lport %i from %s { reply \"%s\" } "
197 "### eggdrop_%s !%" PRId64 "\n", ntohs(saddr->sin_port),
198 inet_ntop(AF_INET, &(saddr->sin_addr), s, INET_ADDRSTRLEN),
199 botuser, pid_file, (int64_t) now);
200 #ifdef IPV6
201 } else if (ss.ss_family == AF_INET6) {
202 struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&ss;
203 fprintf(fd, "lport %i from %s { reply \"%s\" } "
204 "### eggdrop_%s !%" PRId64 "\n", ntohs(saddr->sin6_port),
205 inet_ntop(AF_INET6, &(saddr->sin6_addr), s, INET6_ADDRSTRLEN),
206 botuser, pid_file, (int64_t) now);
207 #endif
208 } else {
209 putlog(LOG_MISC, "*", "IDENT: Error writing oident.conf line");
210 }
211 fclose(fd);
212 } else {
213 putlog(LOG_MISC, "*", "IDENT: Error opening oident.conf for writing");
214 }
215 if (data) {
216 nfree(data);
217 }
218 }
219
ident_builtin_on()220 static void ident_builtin_on()
221 {
222 int idx, s;
223
224 debug0("Ident: Starting ident server.");
225 for (idx = 0; idx < dcc_total; idx++)
226 if (dcc[idx].type == &DCC_IDENTD)
227 return;
228 idx = new_dcc(&DCC_IDENTD, 0);
229 if (idx < 0) {
230 putlog(LOG_MISC, "*", "Ident error: could not get new dcc.");
231 return;
232 }
233 s = open_listen(&ident_port);
234 if (s == -2) {
235 lostdcc(idx);
236 putlog(LOG_MISC, "*", "Ident error: could not bind socket port %i.", ident_port);
237 return;
238 } else if (s == -1) {
239 lostdcc(idx);
240 putlog(LOG_MISC, "*", "Ident error: could not get socket.");
241 return;
242 }
243 dcc[idx].sock = s;
244 dcc[idx].port = ident_port;
245 strcpy(dcc[idx].nick, "(ident)");
246 add_builtins(H_raw, ident_raw);
247 }
248
ident_builtin_off()249 static void ident_builtin_off()
250 {
251 int idx;
252
253 for (idx = 0; idx < dcc_total; idx++)
254 if (dcc[idx].type == &DCC_IDENTD) {
255 debug0("Ident: Stopping ident server.");
256 killsock(dcc[idx].sock);
257 lostdcc(idx);
258 break;
259 }
260 rem_builtins(H_raw, ident_raw);
261 }
262
ident_ident()263 static void ident_ident()
264 {
265 if (ident_method == IDENT_METHOD_OIDENT)
266 ident_oidentd();
267 else if (ident_method == IDENT_METHOD_BUILTIN)
268 ident_builtin_on();
269 }
270
271 static cmd_t ident_event[] = {
272 {"ident", "", (IntFunc) ident_ident, "ident:ident"},
273 {NULL, NULL, NULL, NULL}
274 };
275
ident_close()276 static char *ident_close()
277 {
278 ident_builtin_off();
279 rem_builtins(H_event, ident_event);
280 rem_tcl_ints(identints);
281 module_undepend(MODULE_NAME);
282 return NULL;
283 }
284
285 EXPORT_SCOPE char *ident_start();
286
287 static Function ident_table[] = {
288 (Function) ident_start,
289 (Function) ident_close,
290 NULL,
291 NULL,
292 };
293
ident_start(Function * global_funcs)294 char *ident_start(Function *global_funcs)
295 {
296 global = global_funcs;
297
298 module_register(MODULE_NAME, ident_table, 0, 9);
299
300 if (!module_depend(MODULE_NAME, "eggdrop", 109, 0)) {
301 module_undepend(MODULE_NAME);
302 return "This module requires Eggdrop 1.9.0 or later.";
303 }
304 if (!(server_funcs = module_depend(MODULE_NAME, "server", 1, 0))) {
305 module_undepend(MODULE_NAME);
306 return "This module requires server module 1.0 or later.";
307 }
308
309 add_builtins(H_event, ident_event);
310 add_tcl_ints(identints);
311
312 return NULL;
313 }
314