Merged silc_1_0_branch to trunk.
[silc.git] / lib / silcutil / unix / silcunixsockconn.c
1 /*
2
3   silcunixsockconn.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2005 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 /* Writes data from encrypted buffer to the socket connection. If the
24    data cannot be written at once, it will be written later with a timeout.
25    The data is written from the data section of the buffer, not from head
26    or tail section. This automatically pulls the data section towards end
27    after writing the data. */
28
29 int silc_socket_write(SilcSocketConnection sock)
30 {
31   int ret = 0;
32   int fd = sock->sock;
33   SilcBuffer src = sock->outbuf;
34
35   if (!src)
36     return -1;
37   if (SILC_IS_DISABLED(sock))
38     return -1;
39
40   SILC_LOG_DEBUG(("Writing data to socket %d", fd));
41
42   if (src->len > 0) {
43     ret = write(fd, src->data, src->len);
44     if (ret < 0) {
45       if (errno == EAGAIN || errno == EINTR) {
46         SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
47         return -2;
48       }
49       SILC_LOG_DEBUG(("Cannot write to socket: %s", strerror(errno)));
50       sock->sock_error = errno;
51       return -1;
52     }
53
54     if (ret < src->len) {
55       SILC_LOG_DEBUG(("Wrote data %d of %d bytes, will write rest later",
56                       ret, src->len));
57       silc_buffer_pull(src, ret);
58       return -2;
59     }
60
61     silc_buffer_pull(src, ret);
62   }
63
64   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
65
66   return ret;
67 }
68
69 /* QoS read handler, this will call the read and write events to indicate
70    that data is available again after a timeout. */
71
72 SILC_TASK_CALLBACK(silc_socket_read_qos)
73 {
74   SilcSocketConnectionQos qos = context;
75   SilcSocketConnection sock = qos->sock;
76   qos->applied = TRUE;
77   if (sock->users > 1)
78     silc_schedule_set_listen_fd(qos->schedule, sock->sock,
79                                 (SILC_TASK_READ | SILC_TASK_WRITE), TRUE);
80   else
81     silc_schedule_unset_listen_fd(qos->schedule, sock->sock);
82   qos->applied = FALSE;
83   silc_socket_free(sock);
84 }
85
86 /* Reads data from the socket connection into the incoming data buffer.
87    It reads as much as possible from the socket connection. This returns
88    amount of bytes read or -1 on error or -2 on case where all of the
89    data could not be read at once. */
90
91 int silc_socket_read(SilcSocketConnection sock)
92 {
93   int len = 0;
94   unsigned char buf[SILC_SOCKET_READ_SIZE];
95   int fd = sock->sock;
96
97   if (SILC_IS_DISABLED(sock))
98     return -1;
99
100   /* If QoS was applied to socket then return earlier read data but apply
101      QoS to it too, if necessary. */
102   if (sock->qos) {
103     if (sock->qos->applied) {
104       if (sock->qos->data_len) {
105         /* Pull hidden data since we have it from earlier QoS apply */
106         silc_buffer_pull_tail(sock->inbuf, sock->qos->data_len);
107         len = sock->qos->data_len;
108         sock->qos->data_len = 0;
109       }
110
111       if (sock->inbuf->len - len > sock->qos->read_limit_bytes) {
112         /* Seems we need to apply QoS for the remaining data as well */
113         silc_socket_dup(sock);
114         silc_schedule_task_add(sock->qos->schedule, sock->sock,
115                                silc_socket_read_qos, sock->qos,
116                                sock->qos->limit_sec, sock->qos->limit_usec,
117                                SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
118         silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
119
120         /* Hide the rest of the data from the buffer. */
121         sock->qos->data_len = (sock->inbuf->len - len -
122                                sock->qos->read_limit_bytes);
123         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
124       }
125
126       if (sock->inbuf->len)
127         return sock->inbuf->len;
128     }
129
130     /* If we were called and we have active QoS data pending, return
131        with no data */
132     if (sock->qos->data_len) {
133       silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
134       return -2;
135     }
136   }
137
138   SILC_LOG_DEBUG(("Reading data from socket %d", fd));
139
140   /* Read the data from the socket. */
141   len = read(fd, buf, sizeof(buf));
142   if (len < 0) {
143     if (errno == EAGAIN || errno == EINTR) {
144       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
145       return -2;
146     }
147     SILC_LOG_DEBUG(("Cannot read from socket: %d:%s", fd, strerror(errno)));
148     sock->sock_error = errno;
149     return -1;
150   }
151
152   if (!len)
153     return 0;
154
155   /* Insert the data to the buffer. */
156
157   if (!sock->inbuf)
158     sock->inbuf = silc_buffer_alloc(SILC_SOCKET_BUF_SIZE);
159
160   /* If the data does not fit to the buffer reallocate it */
161   if ((sock->inbuf->end - sock->inbuf->tail) < len)
162     sock->inbuf = silc_buffer_realloc(sock->inbuf, sock->inbuf->truelen +
163                                       (len * 2));
164   silc_buffer_put_tail(sock->inbuf, buf, len);
165   silc_buffer_pull_tail(sock->inbuf, len);
166
167   SILC_LOG_DEBUG(("Read %d bytes", len));
168
169   /* Apply QoS to the read data if necessary */
170   if (sock->qos) {
171     struct timeval curtime;
172     silc_gettimeofday(&curtime);
173
174     /* If we have passed the rate time limit, set our new time limit,
175        and zero the rate limit. */
176     if (!silc_compare_timeval(&curtime, &sock->qos->next_limit)) {
177       curtime.tv_sec++;
178       sock->qos->next_limit = curtime;
179       sock->qos->cur_rate = 0;
180     }
181     sock->qos->cur_rate++;
182
183     /* If we are not withing rate limit apply QoS for the read data */
184     if (sock->qos->cur_rate > sock->qos->read_rate) {
185       silc_socket_dup(sock);
186       silc_schedule_task_add(sock->qos->schedule, sock->sock,
187                              silc_socket_read_qos, sock->qos,
188                              sock->qos->limit_sec, sock->qos->limit_usec,
189                              SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
190       silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
191
192       /* Check the byte limit as well, and do not return more than allowed */
193       if (sock->inbuf->len > sock->qos->read_limit_bytes) {
194         /* Hide the rest of the data from the buffer. */
195         sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
196         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
197         len = sock->inbuf->len;
198       } else {
199         /* Rate limit kicked in, do not return data yet */
200         return -2;
201       }
202     } else {
203       /* Check the byte limit, and do not return more than allowed */
204       if (sock->inbuf->len > sock->qos->read_limit_bytes) {
205         silc_socket_dup(sock);
206         silc_schedule_task_add(sock->qos->schedule, sock->sock,
207                                silc_socket_read_qos, sock->qos,
208                                sock->qos->limit_sec, sock->qos->limit_usec,
209                                SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
210         silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
211
212         /* Hide the rest of the data from the buffer. */
213         sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
214         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
215         len = sock->inbuf->len;
216       }
217     }
218   }
219
220   return len;
221 }
222
223 /* Returns human readable socket error message */
224
225 bool silc_socket_get_error(SilcSocketConnection sock, char *error,
226                            SilcUInt32 error_len)
227 {
228   char *err;
229
230   if (!sock->sock_error)
231     return FALSE;
232
233   err = strerror(sock->sock_error);
234   if (strlen(err) > error_len)
235     return FALSE;
236
237   memset(error, 0, error_len);
238   memcpy(error, err, strlen(err));
239   return TRUE;
240 }