Merged silc_1_0_branch to trunk.
[silc.git] / lib / silcutil / silcsockconn.c
1 /*
2
3   silcsockconn.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2003 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; version 2 of the License.
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 */
19 /* $Id$ */
20
21 #include "silcincludes.h"
22
23 /* Heartbeat context */
24 struct SilcSocketConnectionHBStruct {
25   SilcUInt32 heartbeat;
26   SilcSocketConnectionHBCb hb_callback;
27   void *hb_context;
28   SilcSchedule schedule;
29   SilcTask hb_task;
30   SilcSocketConnection sock;
31 };
32
33 /* Internal async host lookup context. */
34 typedef struct {
35   SilcSocketHostLookupCb callback;
36   void *context;
37   SilcSchedule schedule;
38   SilcSocketConnection sock;
39   bool port;
40 } *SilcSocketHostLookup;
41
42 /* Allocates a new socket connection object. The allocated object is
43    returned to the new_socket argument. */
44
45 void silc_socket_alloc(int sock, SilcSocketType type, void *user_data,
46                        SilcSocketConnection *new_socket)
47 {
48   SILC_LOG_DEBUG(("Allocating new socket connection object"));
49
50   /* Set the pointers. Incoming and outgoing data buffers
51      are allocated by the application when they are first used. */
52   *new_socket = silc_calloc(1, sizeof(**new_socket));
53   (*new_socket)->sock = sock;
54   (*new_socket)->type = type;
55   (*new_socket)->user_data = user_data;
56   (*new_socket)->protocol = NULL;
57   (*new_socket)->flags = 0;
58   (*new_socket)->inbuf = NULL;
59   (*new_socket)->outbuf = NULL;
60   (*new_socket)->users++;
61 }
62
63 /* Free's the Socket connection object. */
64
65 void silc_socket_free(SilcSocketConnection sock)
66 {
67   sock->users--;
68   SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users + 1,
69                   sock->users));
70   if (sock->users < 1) {
71     silc_buffer_free(sock->inbuf);
72     silc_buffer_free(sock->outbuf);
73     if (sock->hb) {
74       silc_schedule_task_del(sock->hb->schedule, sock->hb->hb_task);
75       silc_free(sock->hb);
76     }
77     if (sock->qos) {
78       silc_schedule_task_del_by_context(sock->qos->schedule, sock->qos);
79       silc_free(sock->qos);
80     }
81     silc_free(sock->ip);
82     silc_free(sock->hostname);
83
84     memset(sock, 'F', sizeof(*sock));
85     silc_free(sock);
86   }
87 }
88
89 /* Increase the reference counter. */
90
91 SilcSocketConnection silc_socket_dup(SilcSocketConnection sock)
92 {
93   sock->users++;
94   SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users - 1,
95                   sock->users));
96   return sock;
97 }
98
99 /* Internal timeout callback to perform heartbeat */
100
101 SILC_TASK_CALLBACK(silc_socket_heartbeat)
102 {
103   SilcSocketConnectionHB hb = (SilcSocketConnectionHB)context;
104
105   if (!hb->heartbeat)
106     return;
107
108   if (SILC_IS_DISCONNECTING(hb->sock) ||
109       SILC_IS_DISCONNECTED(hb->sock))
110     return;
111
112   if (hb->hb_callback)
113     hb->hb_callback(hb->sock, hb->hb_context);
114
115   hb->hb_task = silc_schedule_task_add(hb->schedule, hb->sock->sock,
116                                        silc_socket_heartbeat,
117                                        context, hb->heartbeat, 0,
118                                        SILC_TASK_TIMEOUT,
119                                        SILC_TASK_PRI_LOW);
120 }
121
122 /* Sets the heartbeat timeout and prepares the socket for performing
123    heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
124    allocated by the application and will be sent as argument to the
125    `hb_callback' function that is called when the `heartbeat' timeout
126    expires.  The callback `hb_context' won't be touched by the library
127    but will be freed automatically when calling silc_socket_free.  The
128    `schedule' is the application's scheduler. */
129
130 void silc_socket_set_heartbeat(SilcSocketConnection sock,
131                                SilcUInt32 heartbeat,
132                                void *hb_context,
133                                SilcSocketConnectionHBCb hb_callback,
134                                SilcSchedule schedule)
135 {
136   if (sock->hb) {
137     silc_schedule_task_del(schedule, sock->hb->hb_task);
138     silc_free(sock->hb);
139   }
140
141   sock->hb = silc_calloc(1, sizeof(*sock->hb));
142   sock->hb->heartbeat = heartbeat;
143   sock->hb->hb_context = hb_context;
144   sock->hb->hb_callback = hb_callback;
145   sock->hb->schedule = schedule;
146   sock->hb->sock = sock;
147   sock->hb->hb_task = silc_schedule_task_add(schedule, sock->sock,
148                                              silc_socket_heartbeat,
149                                              (void *)sock->hb, heartbeat, 0,
150                                              SILC_TASK_TIMEOUT,
151                                              SILC_TASK_PRI_LOW);
152 }
153
154 /* Sets a "Quality of Service" settings for socket connection `sock'.
155    The `read_rate' specifies the maximum read operations per second.
156    If more read operations are executed the limit will be applied for
157    the reading.  The `read_limit_bytes' specifies the maximum data
158    that is read.  It is guaranteed that silc_socket_read never returns
159    more that `read_limit_bytes' of data.  If more is read the limit
160    will be applied for the reading.  The `limit_sec' and `limit_usec'
161    specifies the limit that is applied if `read_rate' and/or
162    `read_limit_bytes' is reached.  The `schedule' is the application's
163    scheduler. */
164
165 void silc_socket_set_qos(SilcSocketConnection sock,
166                          SilcUInt32 read_rate,
167                          SilcUInt32 read_limit_bytes,
168                          SilcUInt32 limit_sec,
169                          SilcUInt32 limit_usec,
170                          SilcSchedule schedule)
171 {
172   if (!sock)
173     return;
174
175   if (sock->qos && !read_rate && !read_limit_bytes &&
176       !limit_sec && !limit_usec && !schedule) {
177     silc_schedule_task_del_by_context(sock->qos->schedule, sock->qos);
178     silc_free(sock->qos);
179     sock->qos = NULL;
180     return;
181   }
182   if (!schedule)
183     return;
184
185   if (!sock->qos) {
186     sock->qos = silc_calloc(1, sizeof(*sock->qos));
187     if (!sock->qos)
188       return;
189   }
190   sock->qos->read_rate = read_rate;
191   sock->qos->read_limit_bytes = read_limit_bytes;
192   sock->qos->limit_sec = limit_sec;
193   sock->qos->limit_usec = limit_usec;
194   sock->qos->schedule = schedule;
195   memset(&sock->qos->next_limit, 0, sizeof(sock->qos->next_limit));
196   sock->qos->cur_rate = 0;
197   sock->qos->sock = sock;
198 }
199
200 /* Finishing timeout callback that will actually call the user specified
201    host lookup callback. This is executed back in the calling thread and
202    not in the lookup thread. */
203
204 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
205 {
206   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
207
208   SILC_UNSET_HOST_LOOKUP(lookup->sock);
209
210   /* If the reference counter is 1 we know that we are the only one
211      holding the socket and it thus is considered freed. The lookup
212      is cancelled also and we will not call the final callback. */
213   if (lookup->sock->users == 1) {
214     SILC_LOG_DEBUG(("Async host lookup was cancelled"));
215     silc_socket_free(lookup->sock);
216     silc_free(lookup);
217     return;
218   }
219
220   SILC_LOG_DEBUG(("Async host lookup finished"));
221
222   silc_socket_free(lookup->sock);
223
224   /* Call the final callback. */
225   if (lookup->callback)
226     lookup->callback(lookup->sock, lookup->context);
227
228   silc_free(lookup);
229 }
230
231 /* The thread function that performs the actual lookup. */
232
233 static void *silc_socket_host_lookup_start(void *context)
234 {
235   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
236   SilcSocketConnection sock = lookup->sock;
237   SilcSchedule schedule = lookup->schedule;
238
239   if (lookup->port)
240     sock->port = silc_net_get_remote_port(sock->sock);
241
242   silc_net_check_host_by_sock(sock->sock, &sock->hostname, &sock->ip);
243   if (!sock->hostname && sock->ip)
244     sock->hostname = strdup(sock->ip);
245
246   silc_schedule_task_add(schedule, sock->sock,
247                          silc_socket_host_lookup_finish, lookup, 0, 1,
248                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
249   silc_schedule_wakeup(schedule);
250
251   return NULL;
252 }
253
254 /* Performs asynchronous host name and IP address lookups for the
255    specified socket connection. This may be called when the socket
256    connection is created and the full IP address and fully qualified
257    domain name information is desired. The `callback' with `context'
258    will be called after the lookup is performed. The `schedule'
259    is the application's scheduler which the lookup routine needs. If
260    the socket connection is freed during the lookup the library will
261    automatically cancel the lookup and the `callback' will not be called. */
262
263 void silc_socket_host_lookup(SilcSocketConnection sock,
264                              bool port_lookup,
265                              SilcSocketHostLookupCb callback,
266                              void *context,
267                              SilcSchedule schedule)
268 {
269   SilcSocketHostLookup lookup;
270
271   SILC_LOG_DEBUG(("Performing async host lookup"));
272
273   if (SILC_IS_DISCONNECTING(sock) ||
274       SILC_IS_DISCONNECTED(sock))
275     return;
276
277   lookup = silc_calloc(1, sizeof(*lookup));
278   lookup->sock = silc_socket_dup(sock); /* Increase reference counter */
279   lookup->callback = callback;
280   lookup->context = context;
281   lookup->schedule = schedule;
282   lookup->port = port_lookup;
283
284   SILC_SET_HOST_LOOKUP(sock);
285   silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
286 }