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 - 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   SilcUInt32 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);
77     }
78     silc_free(sock->qos);
79     silc_free(sock->ip);
80     silc_free(sock->hostname);
81
82     memset(sock, 'F', sizeof(*sock));
83     silc_free(sock);
84   }
85 }
86
87 /* Increase the reference counter. */
88
89 SilcSocketConnection silc_socket_dup(SilcSocketConnection sock)
90 {
91   sock->users++;
92   SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users - 1,
93                   sock->users));
94   return sock;
95 }
96
97 /* Internal timeout callback to perform heartbeat */
98
99 SILC_TASK_CALLBACK(silc_socket_heartbeat)
100 {
101   SilcSocketConnectionHB hb = (SilcSocketConnectionHB)context;
102
103   if (!hb->heartbeat)
104     return;
105
106   if (SILC_IS_DISCONNECTING(hb->sock) ||
107       SILC_IS_DISCONNECTED(hb->sock))
108     return;
109
110   if (hb->hb_callback)
111     hb->hb_callback(hb->sock, hb->hb_context);
112
113   hb->hb_task = silc_schedule_task_add(hb->schedule, hb->sock->sock, 
114                                        silc_socket_heartbeat,
115                                        context, hb->heartbeat, 0,
116                                        SILC_TASK_TIMEOUT,
117                                        SILC_TASK_PRI_LOW);
118 }
119
120 /* Sets the heartbeat timeout and prepares the socket for performing
121    heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
122    allocated by the application and will be sent as argument to the
123    `hb_callback' function that is called when the `heartbeat' timeout
124    expires.  The callback `hb_context' won't be touched by the library
125    but will be freed automatically when calling silc_socket_free.  The
126    `schedule' is the application's scheduler. */
127
128 void silc_socket_set_heartbeat(SilcSocketConnection sock, 
129                                SilcUInt32 heartbeat,
130                                void *hb_context,
131                                SilcSocketConnectionHBCb hb_callback,
132                                SilcSchedule schedule)
133 {
134   if (sock->hb) {
135     silc_schedule_task_del(schedule, sock->hb->hb_task);
136     silc_free(sock->hb);
137   }
138
139   sock->hb = silc_calloc(1, sizeof(*sock->hb));
140   sock->hb->heartbeat = heartbeat;
141   sock->hb->hb_context = hb_context;
142   sock->hb->hb_callback = hb_callback;
143   sock->hb->schedule = schedule;
144   sock->hb->sock = sock;
145   sock->hb->hb_task = silc_schedule_task_add(schedule, sock->sock,
146                                              silc_socket_heartbeat,
147                                              (void *)sock->hb, heartbeat, 0,
148                                              SILC_TASK_TIMEOUT,
149                                              SILC_TASK_PRI_LOW);
150 }
151
152 /* Sets a "Quality of Service" settings for socket connection `sock'.
153    The `read_rate' specifies the maximum read operations per second.
154    If more read operations are executed the limit will be applied for
155    the reading.  The `read_limit_bytes' specifies the maximum data
156    that is read.  It is guaranteed that silc_socket_read never returns
157    more that `read_limit_bytes' of data.  If more is read the limit
158    will be applied for the reading.  The `limit_sec' and `limit_usec'
159    specifies the limit that is applied if `read_rate' and/or 
160    `read_limit_bytes' is reached.  The `schedule' is the application's
161    scheduler. */
162
163 void silc_socket_set_qos(SilcSocketConnection sock, 
164                          SilcUInt32 read_rate,
165                          SilcUInt32 read_limit_bytes,
166                          SilcUInt32 limit_sec,
167                          SilcUInt32 limit_usec,
168                          SilcSchedule schedule)
169 {
170   if (!sock->qos) {
171     sock->qos = silc_calloc(1, sizeof(*sock->qos));
172     if (!sock->qos)
173       return;
174   }
175   sock->qos->read_rate = read_rate;
176   sock->qos->read_limit_bytes = read_limit_bytes;
177   sock->qos->limit_sec = limit_sec;
178   sock->qos->limit_usec = limit_usec;
179   sock->qos->schedule = schedule;
180   memset(&sock->qos->next_limit, 0, sizeof(sock->qos->next_limit));
181   sock->qos->cur_rate = 0;
182 }
183
184 /* Finishing timeout callback that will actually call the user specified
185    host lookup callback. This is executed back in the calling thread and
186    not in the lookup thread. */
187
188 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
189 {
190   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
191
192   SILC_UNSET_HOST_LOOKUP(lookup->sock);
193
194   /* If the reference counter is 1 we know that we are the only one
195      holding the socket and it thus is considered freed. The lookup
196      is cancelled also and we will not call the final callback. */
197   if (lookup->sock->users == 1) {
198     SILC_LOG_DEBUG(("Async host lookup was cancelled"));
199     silc_socket_free(lookup->sock);
200     silc_free(lookup);
201     return;
202   }
203
204   SILC_LOG_DEBUG(("Async host lookup finished"));
205
206   silc_socket_free(lookup->sock);
207
208   /* Call the final callback. */
209   if (lookup->callback)
210     lookup->callback(lookup->sock, lookup->context);
211
212   silc_free(lookup);
213 }
214
215 /* The thread function that performs the actual lookup. */
216
217 static void *silc_socket_host_lookup_start(void *context)
218 {
219   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
220   SilcSocketConnection sock = lookup->sock;
221   SilcSchedule schedule = lookup->schedule;
222
223   if (lookup->port)
224     sock->port = silc_net_get_remote_port(sock->sock);
225
226   silc_net_check_host_by_sock(sock->sock, &sock->hostname, &sock->ip);  
227   if (!sock->hostname && sock->ip)
228     sock->hostname = strdup(sock->ip);
229
230   silc_schedule_task_add(schedule, sock->sock,
231                          silc_socket_host_lookup_finish, lookup, 0, 1,
232                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
233   silc_schedule_wakeup(schedule);
234
235   return NULL;
236 }
237
238 /* Performs asynchronous host name and IP address lookups for the
239    specified socket connection. This may be called when the socket
240    connection is created and the full IP address and fully qualified
241    domain name information is desired. The `callback' with `context'
242    will be called after the lookup is performed. The `schedule'
243    is the application's scheduler which the lookup routine needs. If
244    the socket connection is freed during the lookup the library will
245    automatically cancel the lookup and the `callback' will not be called. */
246
247 void silc_socket_host_lookup(SilcSocketConnection sock,
248                              bool port_lookup,
249                              SilcSocketHostLookupCb callback,
250                              void *context,
251                              SilcSchedule schedule)
252 {
253   SilcSocketHostLookup lookup;
254
255   SILC_LOG_DEBUG(("Performing async host lookup"));
256
257   if (SILC_IS_DISCONNECTING(sock) ||
258       SILC_IS_DISCONNECTED(sock))
259     return;
260
261   lookup = silc_calloc(1, sizeof(*lookup));
262   lookup->sock = silc_socket_dup(sock); /* Increase reference counter */
263   lookup->callback = callback;
264   lookup->context = context;
265   lookup->schedule = schedule;
266   lookup->port = port_lookup;
267
268   SILC_SET_HOST_LOOKUP(sock);
269   silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
270 }