1 /********************************************************************\
2   * BitlBee -- An IRC to other IM-networks gateway                     *
3   *                                                                    *
4   * Copyright 2002-2004 Wilmer van der Gaast and others                *
5   \********************************************************************/
6 
7 /* Main file                                                            */
8 
9 /*
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19 
20   You should have received a copy of the GNU General Public License with
21   the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22   if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23   Fifth Floor, Boston, MA  02110-1301  USA
24 */
25 
26 #define BITLBEE_CORE
27 #include "bitlbee.h"
28 #include "commands.h"
29 #include "protocols/nogaim.h"
30 #include "help.h"
31 #include "ipc.h"
32 #include <signal.h>
33 #include <stdio.h>
34 #include <errno.h>
35 
36 static gboolean bitlbee_io_new_client(gpointer data, gint fd, b_input_condition condition);
37 
try_listen(struct addrinfo * res)38 static gboolean try_listen(struct addrinfo *res)
39 {
40 	int i;
41 
42 	global.listen_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
43 	if (global.listen_socket < 0) {
44 		log_error("socket");
45 		return FALSE;
46 	}
47 
48 #ifdef IPV6_V6ONLY
49 	if (res->ai_family == AF_INET6) {
50 		i = 0;
51 		setsockopt(global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY,
52 		           (char *) &i, sizeof(i));
53 	}
54 #endif
55 
56 	/* TIME_WAIT (?) sucks.. */
57 	i = 1;
58 	setsockopt(global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
59 
60 	i = bind(global.listen_socket, res->ai_addr, res->ai_addrlen);
61 	if (i == -1) {
62 		closesocket(global.listen_socket);
63 		global.listen_socket = -1;
64 
65 		log_error("bind");
66 		return FALSE;
67 	}
68 
69 	return TRUE;
70 }
71 
bitlbee_daemon_init()72 int bitlbee_daemon_init()
73 {
74 	struct addrinfo *res, hints, *addrinfo_bind;
75 	int i;
76 	FILE *fp;
77 
78 	log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
79 	log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
80 
81 	memset(&hints, 0, sizeof(hints));
82 	hints.ai_family = PF_UNSPEC;
83 	hints.ai_socktype = SOCK_STREAM;
84 	hints.ai_flags = AI_PASSIVE
85 #ifdef AI_ADDRCONFIG
86 	                 /* Disabled as it may be doing more harm than good: this flag
87 	                    ignores IPv6 addresses on lo (which seems reasonable), but
88 	                    the result is that some clients (including irssi) try to
89 	                    connect to ::1 and fail.
90 	                                | AI_ADDRCONFIG */
91 #endif
92 	;
93 
94 	i = getaddrinfo(global.conf->iface_in, global.conf->port, &hints, &addrinfo_bind);
95 	if (i) {
96 		log_message(LOGLVL_ERROR, "Couldn't parse address `%s': %s",
97 		            global.conf->iface_in, gai_strerror(i));
98 		return -1;
99 	}
100 
101 	global.listen_socket = -1;
102 
103 	/* Try IPv6 first (which will become an IPv6+IPv4 socket). */
104 	for (res = addrinfo_bind; res; res = res->ai_next) {
105 		if (res->ai_family == AF_INET6 && try_listen(res)) {
106 			break;
107 		}
108 	}
109 
110 	/* The rest (so IPv4, I guess). */
111 	if (res == NULL) {
112 		for (res = addrinfo_bind; res; res = res->ai_next) {
113 			if (res->ai_family != AF_INET6 && try_listen(res)) {
114 				break;
115 			}
116 		}
117 	}
118 
119 	freeaddrinfo(addrinfo_bind);
120 
121 	if (global.listen_socket == -1 && errno == EADDRINUSE) {
122 		log_message(LOGLVL_ERROR, "Can't listen on port %s. Is another bitlbee already running?",
123 		            global.conf->port);
124 		return(-1);
125 	}
126 
127 	i = listen(global.listen_socket, 10);
128 	if (i == -1) {
129 		log_error("listen");
130 		return(-1);
131 	}
132 
133 	global.listen_watch_source_id = b_input_add(global.listen_socket, B_EV_IO_READ, bitlbee_io_new_client, NULL);
134 
135 	if (!global.conf->nofork) {
136 		i = fork();
137 		if (i == -1) {
138 			log_error("fork");
139 			return(-1);
140 		} else if (i != 0) {
141 			exit(0);
142 		}
143 
144 		setsid();
145 		i = chdir("/");
146 		/* Don't use i, just make gcc happy. :-/ */
147 
148 		if (getenv("_BITLBEE_RESTART_STATE") == NULL) {
149 			for (i = 0; i < 3; i++) {
150 				if (close(i) == 0) {
151 					/* Keep something bogus on those fd's just in case. */
152 					open("/dev/null", O_WRONLY);
153 				}
154 			}
155 		}
156 	}
157 
158 	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
159 		ipc_master_load_state(getenv("_BITLBEE_RESTART_STATE"));
160 	}
161 
162 	if (global.conf->runmode == RUNMODE_DAEMON || global.conf->runmode == RUNMODE_FORKDAEMON) {
163 		ipc_master_listen_socket();
164 	}
165 
166 	if ((fp = fopen(global.conf->pidfile, "w"))) {
167 		fprintf(fp, "%d\n", (int) getpid());
168 		fclose(fp);
169 	} else {
170 		log_message(LOGLVL_WARNING, "Warning: Couldn't write PID to `%s'", global.conf->pidfile);
171 	}
172 
173 	if (!global.conf->nofork) {
174 		log_link(LOGLVL_ERROR, LOGOUTPUT_SYSLOG);
175 		log_link(LOGLVL_WARNING, LOGOUTPUT_SYSLOG);
176 	}
177 
178 	return(0);
179 }
180 
bitlbee_inetd_init()181 int bitlbee_inetd_init()
182 {
183 	if (!irc_new(0)) {
184 		return(1);
185 	}
186 
187 	return(0);
188 }
189 
bitlbee_io_current_client_read(gpointer data,gint fd,b_input_condition cond)190 gboolean bitlbee_io_current_client_read(gpointer data, gint fd, b_input_condition cond)
191 {
192 	irc_t *irc = data;
193 	char line[513];
194 	int st;
195 
196 	st = read(irc->fd, line, sizeof(line) - 1);
197 	if (st == 0) {
198 		irc_abort(irc, 1, "Connection reset by peer");
199 		return FALSE;
200 	} else if (st < 0) {
201 		if (sockerr_again()) {
202 			return TRUE;
203 		} else {
204 			irc_abort(irc, 1, "Read error: %s", strerror(errno));
205 			return FALSE;
206 		}
207 	}
208 
209 	line[st] = '\0';
210 	if (irc->readbuffer == NULL) {
211 		irc->readbuffer = g_strdup(line);
212 	} else {
213 		irc->readbuffer = g_renew(char, irc->readbuffer, strlen(irc->readbuffer) + strlen(line) + 1);
214 		strcpy((irc->readbuffer + strlen(irc->readbuffer)), line);
215 	}
216 
217 	irc_process(irc);
218 
219 	/* Normally, irc_process() shouldn't call irc_free() but irc_abort(). Just in case: */
220 	if (!g_slist_find(irc_connection_list, irc)) {
221 		log_message(LOGLVL_WARNING, "Abnormal termination of connection with fd %d.", fd);
222 		return FALSE;
223 	}
224 
225 	/* Very naughty, go read the RFCs! >:) */
226 	if (irc->readbuffer && (strlen(irc->readbuffer) > 1024)) {
227 		irc_abort(irc, 0, "Maximum line length exceeded");
228 		return FALSE;
229 	}
230 
231 	return TRUE;
232 }
233 
bitlbee_io_current_client_write(gpointer data,gint fd,b_input_condition cond)234 gboolean bitlbee_io_current_client_write(gpointer data, gint fd, b_input_condition cond)
235 {
236 	irc_t *irc = data;
237 	int st, size;
238 
239 	size = irc->sendbuffer->len;
240 
241 	if (size == 0) {
242 		return FALSE;
243 	}
244 
245 	st = write(irc->fd, irc->sendbuffer->str, size);
246 
247 	if (st == 0 || (st < 0 && !sockerr_again())) {
248 		irc_abort(irc, 1, "Write error: %s", strerror(errno));
249 		return FALSE;
250 	} else if (st < 0) { /* && sockerr_again() */
251 		return TRUE;
252 	}
253 
254 	if (st == size) {
255 		g_string_truncate(irc->sendbuffer, 0);
256 		irc->w_watch_source_id = 0;
257 
258 		return FALSE;
259 	} else {
260 		g_string_erase(irc->sendbuffer, 0, st);
261 
262 		return TRUE;
263 	}
264 }
265 
bitlbee_io_new_client(gpointer data,gint fd,b_input_condition condition)266 static gboolean bitlbee_io_new_client(gpointer data, gint fd, b_input_condition condition)
267 {
268 	socklen_t size = sizeof(struct sockaddr_in);
269 	struct sockaddr_in conn_info;
270 	int new_socket = accept(global.listen_socket, (struct sockaddr *) &conn_info, &size);
271 
272 	if (new_socket == -1) {
273 		log_message(LOGLVL_WARNING, "Could not accept new connection: %s", strerror(errno));
274 		return TRUE;
275 	}
276 
277 	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
278 		pid_t client_pid = 0;
279 		int fds[2];
280 
281 		if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
282 			log_message(LOGLVL_WARNING, "Could not create IPC socket for client: %s", strerror(errno));
283 			fds[0] = fds[1] = -1;
284 		}
285 
286 		sock_make_nonblocking(fds[0]);
287 		sock_make_nonblocking(fds[1]);
288 
289 		client_pid = fork();
290 
291 		if (client_pid > 0 && fds[0] != -1) {
292 			struct bitlbee_child *child;
293 
294 			/* TODO: Stuff like this belongs in ipc.c. */
295 			child = g_new0(struct bitlbee_child, 1);
296 			child->pid = client_pid;
297 			child->ipc_fd = fds[0];
298 			child->ipc_inpa = b_input_add(child->ipc_fd, B_EV_IO_READ, ipc_master_read, child);
299 			child->to_fd = -1;
300 			child_list = g_slist_append(child_list, child);
301 
302 			log_message(LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid);
303 
304 			/* Close some things we don't need in the parent process. */
305 			close(new_socket);
306 			close(fds[1]);
307 		} else if (client_pid == 0) {
308 			irc_t *irc;
309 
310 			b_main_init();
311 
312 			/* Close the listening socket, we're a client. */
313 			close(global.listen_socket);
314 			b_event_remove(global.listen_watch_source_id);
315 
316 			/* Make a new pipe for the shutdown signal handler */
317 			sighandler_shutdown_setup();
318 
319 			/* Make the connection. */
320 			irc = irc_new(new_socket);
321 
322 			/* We can store the IPC fd there now. */
323 			global.listen_socket = fds[1];
324 			global.listen_watch_source_id = b_input_add(fds[1], B_EV_IO_READ, ipc_child_read, irc);
325 
326 			close(fds[0]);
327 
328 			ipc_master_free_all();
329 		}
330 	} else {
331 		log_message(LOGLVL_INFO, "Creating new connection with fd %d.", new_socket);
332 		irc_new(new_socket);
333 	}
334 
335 	return TRUE;
336 }
337 
bitlbee_shutdown(gpointer data,gint fd,b_input_condition cond)338 gboolean bitlbee_shutdown(gpointer data, gint fd, b_input_condition cond)
339 {
340 	/* Send the message here with now=TRUE to ensure it arrives */
341 	irc_write_all(TRUE, "ERROR :Closing link: BitlBee server shutting down");
342 
343 	/* Try to save data for all active connections (if desired). */
344 	while (irc_connection_list != NULL) {
345 		irc_abort(irc_connection_list->data, TRUE, NULL);
346 	}
347 
348 	/* We'll only reach this point when not running in inetd mode: */
349 	b_main_quit();
350 
351 	return FALSE;
352 }
353