6af87a96fac464da4be0e2bff820605772ea01d4
[silc.git] / apps / irssi / src / core / net-nonblock.c
1 /*
2  net-nonblock.c : Nonblocking net_connect()
3
4     Copyright (C) 1998-2000 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22
23 #include <signal.h>
24
25 #include "pidwait.h"
26 #include "net-nonblock.h"
27
28 typedef struct {
29         NET_CALLBACK func;
30         void *data;
31
32         GIOChannel *pipes[2];
33         int port;
34         IPADDR *my_ip;
35         int tag;
36 } SIMPLE_THREAD_REC;
37
38 #define is_fatal_error(err) \
39         (err != 0 && err != G_IO_ERROR_AGAIN && errno != EINTR)
40
41 static int g_io_channel_write_block(GIOChannel *channel, void *data, int len)
42 {
43         unsigned int ret;
44         int err, sent;
45
46         sent = 0;
47         do {
48                 err = g_io_channel_write(channel, (char *) data + sent,
49                                          len-sent, &ret);
50                 sent += ret;
51         } while (sent < len && !is_fatal_error(err));
52
53         return err != 0 ? -1 : 0;
54 }
55
56 static int g_io_channel_read_block(GIOChannel *channel, void *data, int len)
57 {
58         time_t maxwait;
59         unsigned int ret;
60         int err, received;
61
62         maxwait = time(NULL)+2;
63         received = 0;
64         do {
65                 err = g_io_channel_read(channel, (char *) data + received,
66                                         len-received, &ret);
67                 received += ret;
68         } while (received < len && time(NULL) < maxwait &&
69                  (ret != 0 || !is_fatal_error(err)));
70
71         return received < len ? -1 : 0;
72 }
73
74 /* nonblocking gethostbyname(), ip (IPADDR) + error (int, 0 = not error) is
75    written to pipe when found PID of the resolver child is returned */
76 int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe)
77 {
78         RESOLVED_IP_REC rec;
79         const char *errorstr;
80 #ifndef WIN32
81         int pid;
82 #endif
83
84         g_return_val_if_fail(addr != NULL, FALSE);
85
86 #ifndef WIN32
87         pid = fork();
88         if (pid > 0) {
89                 /* parent */
90                 pidwait_add(pid);
91                 return pid;
92         }
93
94         if (pid != 0) {
95                 /* failed! */
96                 g_warning("net_connect_thread(): fork() failed! "
97                           "Using blocking resolving");
98         }
99 #endif
100
101         /* child */
102         memset(&rec, 0, sizeof(rec));
103         rec.error = net_gethostbyname(addr, &rec.ip4, &rec.ip6);
104         if (rec.error == 0) {
105                 errorstr = NULL;
106         } else {
107                 errorstr = net_gethosterror(rec.error);
108                 rec.errlen = errorstr == NULL ? 0 : strlen(errorstr)+1;
109         }
110
111         g_io_channel_write_block(pipe, &rec, sizeof(rec));
112         if (rec.errlen != 0)
113                 g_io_channel_write_block(pipe, (void *) errorstr, rec.errlen);
114
115 #ifndef WIN32
116         if (pid == 0)
117                 _exit(99);
118 #endif
119
120         /* we used blocking lookup */
121         return 0;
122 }
123
124 /* get the resolved IP address */
125 int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec)
126 {
127         rec->error = -1;
128         rec->errorstr = NULL;
129
130 #ifndef WIN32
131         fcntl(g_io_channel_unix_get_fd(pipe), F_SETFL, O_NONBLOCK);
132 #endif
133
134         /* get ip+error */
135         if (g_io_channel_read_block(pipe, rec, sizeof(*rec)) == -1) {
136                 rec->errorstr = g_strdup_printf("Host name lookup: %s",
137                                                 g_strerror(errno));
138                 return -1;
139         }
140
141         if (rec->error) {
142                 /* read error string, if we can't read everything for some
143                    reason, just ignore it. */
144                 rec->errorstr = g_malloc0(rec->errlen+1);
145                 g_io_channel_read_block(pipe, rec->errorstr, rec->errlen);
146         }
147
148         return 0;
149 }
150
151 /* Get host name, call func when finished */
152 int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data)
153 {
154         /* FIXME: not implemented */
155         return FALSE;
156 }
157
158 /* Kill the resolver child */
159 void net_disconnect_nonblock(int pid)
160 {
161         g_return_if_fail(pid > 0);
162
163 #ifndef WIN32
164         kill(pid, SIGKILL);
165 #endif
166 }
167
168 static void simple_init(SIMPLE_THREAD_REC *rec, GIOChannel *handle)
169 {
170         g_return_if_fail(rec != NULL);
171
172         g_source_remove(rec->tag);
173
174         if (net_geterror(handle) != 0) {
175                 /* failed */
176                 g_io_channel_close(handle);
177                 g_io_channel_unref(handle);
178                 handle = NULL;
179         }
180
181         rec->func(handle, rec->data);
182         g_free(rec);
183 }
184
185 static void simple_readpipe(SIMPLE_THREAD_REC *rec, GIOChannel *pipe)
186 {
187         RESOLVED_IP_REC iprec;
188         GIOChannel *handle;
189         IPADDR *ip;
190
191         g_return_if_fail(rec != NULL);
192
193         g_source_remove(rec->tag);
194
195         net_gethostbyname_return(pipe, &iprec);
196         g_free_not_null(iprec.errorstr);
197
198         g_io_channel_close(rec->pipes[0]);
199         g_io_channel_unref(rec->pipes[0]);
200         g_io_channel_close(rec->pipes[1]);
201         g_io_channel_unref(rec->pipes[1]);
202
203         ip = iprec.ip4.family != 0 ? &iprec.ip4 : &iprec.ip6;
204         handle = iprec.error == -1 ? NULL :
205                 net_connect_ip(ip, rec->port, rec->my_ip);
206
207         g_free_not_null(rec->my_ip);
208
209         if (handle == NULL) {
210                 /* failed */
211                 rec->func(NULL, rec->data);
212                 g_free(rec);
213                 return;
214         }
215
216         rec->tag = g_input_add(handle, G_INPUT_READ | G_INPUT_WRITE,
217                                (GInputFunction) simple_init, rec);
218 }
219
220 /* Connect to server, call func when finished */
221 int net_connect_nonblock(const char *server, int port, const IPADDR *my_ip,
222                          NET_CALLBACK func, void *data)
223 {
224         SIMPLE_THREAD_REC *rec;
225         int fd[2];
226
227         g_return_val_if_fail(server != NULL, FALSE);
228         g_return_val_if_fail(func != NULL, FALSE);
229
230         if (pipe(fd) != 0) {
231                 g_warning("net_connect_nonblock(): pipe() failed.");
232                 return FALSE;
233         }
234
235         rec = g_new0(SIMPLE_THREAD_REC, 1);
236         rec->port = port;
237         if (my_ip != NULL) {
238                 rec->my_ip = g_malloc(sizeof(IPADDR));
239                 memcpy(rec->my_ip, my_ip, sizeof(IPADDR));
240         }
241         rec->func = func;
242         rec->data = data;
243         rec->pipes[0] = g_io_channel_unix_new(fd[0]);
244         rec->pipes[1] = g_io_channel_unix_new(fd[1]);
245
246         /* start nonblocking host name lookup */
247         net_gethostbyname_nonblock(server, rec->pipes[1]);
248         rec->tag = g_input_add(rec->pipes[0], G_INPUT_READ,
249                                (GInputFunction) simple_readpipe, rec);
250
251         return TRUE;
252 }