updates.
[silc.git] / lib / silcutil / silcsockconn.c
1 /*
2
3   silcsockconn.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23
24 /* Heartbeat context */
25 struct SilcSocketConnectionHBStruct {
26   uint32 heartbeat;
27   SilcSocketConnectionHBCb hb_callback;
28   void *hb_context;
29   SilcSchedule schedule;
30   SilcTask hb_task;
31   SilcSocketConnection sock;
32 };
33
34 /* Internal async host lookup context. */
35 typedef struct {
36   SilcSocketHostLookupCb callback;
37   void *context;
38   SilcSchedule schedule;
39   SilcSocketConnection sock;
40   bool port;
41 } *SilcSocketHostLookup;
42
43 /* Allocates a new socket connection object. The allocated object is 
44    returned to the new_socket argument. */
45
46 void silc_socket_alloc(int sock, SilcSocketType type, void *user_data, 
47                        SilcSocketConnection *new_socket)
48 {
49   SILC_LOG_DEBUG(("Allocating new socket connection object"));
50
51   /* Set the pointers. Incoming and outgoing data buffers
52      are allocated by the application when they are first used. */
53   *new_socket = silc_calloc(1, sizeof(**new_socket));
54   (*new_socket)->sock = sock;
55   (*new_socket)->type = type;
56   (*new_socket)->user_data = user_data;
57   (*new_socket)->protocol = NULL;
58   (*new_socket)->flags = 0;
59   (*new_socket)->inbuf = NULL;
60   (*new_socket)->outbuf = NULL;
61   (*new_socket)->users++;
62 }
63
64 /* Free's the Socket connection object. */
65
66 void silc_socket_free(SilcSocketConnection sock)
67 {
68   sock->users--;
69   SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users + 1,
70                   sock->users));
71   if (sock->users < 1) {
72     silc_buffer_free(sock->inbuf);
73     silc_buffer_free(sock->outbuf);
74     if (sock->hb) {
75       silc_schedule_task_del(sock->hb->schedule, sock->hb->hb_task);
76       silc_free(sock->hb->hb_context);
77       silc_free(sock->hb);
78     }
79
80     memset(sock, 'F', sizeof(*sock));
81     silc_free(sock);
82   }
83 }
84
85 /* Increase the reference counter. */
86
87 SilcSocketConnection silc_socket_dup(SilcSocketConnection sock)
88 {
89   sock->users++;
90   SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users - 1,
91                   sock->users));
92   return sock;
93 }
94
95 /* Internal timeout callback to perform heartbeat */
96
97 SILC_TASK_CALLBACK(silc_socket_heartbeat)
98 {
99   SilcSocketConnectionHB hb = (SilcSocketConnectionHB)context;
100
101   if (!hb->heartbeat)
102     return;
103
104   if (hb->hb_callback)
105     hb->hb_callback(hb->sock, hb->hb_context);
106
107   hb->hb_task = silc_schedule_task_add(hb->schedule, hb->sock->sock, 
108                                        silc_socket_heartbeat,
109                                        context, hb->heartbeat, 0,
110                                        SILC_TASK_TIMEOUT,
111                                        SILC_TASK_PRI_LOW);
112 }
113
114 /* Sets the heartbeat timeout and prepares the socket for performing
115    heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
116    allocated by the application and will be sent as argument to the
117    `hb_callback' function that is called when the `heartbeat' timeout
118    expires.  The callback `hb_context' won't be touched by the library
119    but will be freed automatically when calling silc_socket_free.  The
120    `schedule' is the application's scheduler. */
121
122 void silc_socket_set_heartbeat(SilcSocketConnection sock, 
123                                uint32 heartbeat,
124                                void *hb_context,
125                                SilcSocketConnectionHBCb hb_callback,
126                                SilcSchedule schedule)
127 {
128   if (sock->hb) {
129     silc_schedule_task_del(schedule, sock->hb->hb_task);
130     silc_free(sock->hb->hb_context);
131     silc_free(sock->hb);
132   }
133
134   sock->hb = silc_calloc(1, sizeof(*sock->hb));
135   sock->hb->heartbeat = heartbeat;
136   sock->hb->hb_context = hb_context;
137   sock->hb->hb_callback = hb_callback;
138   sock->hb->schedule = schedule;
139   sock->hb->sock = sock;
140   sock->hb->hb_task = silc_schedule_task_add(schedule, sock->sock,
141                                              silc_socket_heartbeat,
142                                              (void *)sock->hb, heartbeat, 0,
143                                              SILC_TASK_TIMEOUT,
144                                              SILC_TASK_PRI_LOW);
145 }
146
147 /* Finishing timeout callback that will actually call the user specified
148    host lookup callback. This is executed back in the calling thread and
149    not in the lookup thread. */
150
151 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
152 {
153   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
154
155   SILC_UNSET_HOST_LOOKUP(lookup->sock);
156
157   /* If the reference counter is 1 we know that we are the only one
158      holding the socket and it thus is considered freed. The lookup
159      is cancelled also and we will not call the final callback. */
160   if (lookup->sock->users == 1) {
161     SILC_LOG_DEBUG(("Async host lookup was cancelled"));
162     silc_free(lookup);
163     silc_socket_free(lookup->sock);
164     return;
165   }
166
167   SILC_LOG_DEBUG(("Async host lookup finished"));
168
169   silc_socket_free(lookup->sock);
170
171   /* Call the final callback. */
172   if (lookup->callback)
173     lookup->callback(lookup->sock, lookup->context);
174
175   silc_free(lookup);
176 }
177
178 /* The thread function that performs the actual lookup. */
179
180 static void *silc_socket_host_lookup_start(void *context)
181 {
182   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
183   SilcSocketConnection sock = lookup->sock;
184
185   if (lookup->port)
186     sock->port = silc_net_get_remote_port(sock->sock);
187
188   silc_net_check_host_by_sock(sock->sock, &sock->hostname, &sock->ip);  
189   if (!sock->hostname && sock->ip)
190     sock->hostname = strdup(sock->ip);
191
192   silc_schedule_task_add(lookup->schedule, sock->sock,
193                          silc_socket_host_lookup_finish, lookup, 0, 1,
194                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
195   silc_schedule_wakeup(lookup->schedule);
196
197   return NULL;
198 }
199
200 /* Performs asynchronous host name and IP address lookups for the
201    specified socket connection. This may be called when the socket
202    connection is created and the full IP address and fully qualified
203    domain name information is desired. The `callback' with `context'
204    will be called after the lookup is performed. The `schedule'
205    is the application's scheduler which the lookup routine needs. If
206    the socket connection is freed during the lookup the library will
207    automatically cancel the lookup and the `callback' will not be called. */
208
209 void silc_socket_host_lookup(SilcSocketConnection sock,
210                              bool port_lookup,
211                              SilcSocketHostLookupCb callback,
212                              void *context,
213                              SilcSchedule schedule)
214 {
215   SilcSocketHostLookup lookup;
216
217   SILC_LOG_DEBUG(("Performing async host lookup"));
218
219   lookup = silc_calloc(1, sizeof(*lookup));
220   lookup->sock = silc_socket_dup(sock); /* Increase reference counter */
221   lookup->callback = callback;
222   lookup->context = context;
223   lookup->schedule = schedule;
224   lookup->port = port_lookup;
225
226   SILC_SET_HOST_LOOKUP(sock);
227
228 #ifdef SILC_THREADS
229   silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
230 #else
231   silc_socket_host_lookup_start((void *)lookup);
232 #endif
233 }