5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2001 Pekka Riikonen
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.
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.
22 #include "silcincludes.h"
24 /* Writes data from encrypted buffer to the socket connection. If the
25 data cannot be written at once, it will be written later with a timeout.
26 The data is written from the data section of the buffer, not from head
27 or tail section. This automatically pulls the data section towards end
28 after writing the data. */
30 int silc_socket_write(SilcSocketConnection sock)
34 SilcBuffer src = sock->outbuf;
36 if (SILC_IS_DISABLED(sock))
39 SILC_LOG_DEBUG(("Writing data to socket %d", fd));
42 ret = write(fd, src->data, src->len);
44 if (errno == EAGAIN) {
45 SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
48 SILC_LOG_DEBUG(("Cannot write to socket: %s", strerror(errno)));
49 sock->sock_error = errno;
54 SILC_LOG_DEBUG(("Wrote data %d of %d bytes, will write rest later",
56 silc_buffer_pull(src, ret);
60 silc_buffer_pull(src, ret);
63 SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
68 /* QoS read handler, this will call the read and write events to indicate
69 that data is available again after a timeout */
71 SILC_TASK_CALLBACK(silc_socket_read_qos)
73 SilcSocketConnection sock = context;
74 sock->qos->applied = TRUE;
75 silc_schedule_set_listen_fd(sock->qos->schedule, sock->sock,
76 (SILC_TASK_READ | SILC_TASK_WRITE), TRUE);
77 sock->qos->applied = FALSE;
78 silc_socket_free(sock);
81 /* Reads data from the socket connection into the incoming data buffer.
82 It reads as much as possible from the socket connection. This returns
83 amount of bytes read or -1 on error or -2 on case where all of the
84 data could not be read at once. */
86 int silc_socket_read(SilcSocketConnection sock)
89 unsigned char buf[SILC_SOCKET_READ_SIZE];
92 if (SILC_IS_DISABLED(sock))
95 /* If QoS was applied to socket then return earlier read data but apply
96 QoS to it too, if necessary. */
98 if (sock->qos->applied) {
99 if (sock->qos->data_len) {
100 /* Pull hidden data since we have it from earlier QoS apply */
101 silc_buffer_pull_tail(sock->inbuf, sock->qos->data_len);
102 len = sock->qos->data_len;
103 sock->qos->data_len = 0;
106 if (sock->inbuf->len - len > sock->qos->read_limit_bytes) {
107 /* Seems we need to apply QoS for the remaining data as well */
108 silc_schedule_task_add(sock->qos->schedule, sock->sock,
109 silc_socket_read_qos, silc_socket_dup(sock),
110 sock->qos->limit_sec, sock->qos->limit_usec,
111 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
112 silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
114 /* Hide the rest of the data from the buffer. */
115 sock->qos->data_len = (sock->inbuf->len - len -
116 sock->qos->read_limit_bytes);
117 silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
120 if (sock->inbuf->len)
121 return sock->inbuf->len;
124 /* If we were called and we have active QoS data pending, return
126 if (sock->qos->data_len) {
127 silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
132 SILC_LOG_DEBUG(("Reading data from socket %d", fd));
134 /* Read the data from the socket. */
135 len = read(fd, buf, sizeof(buf));
137 if (errno == EAGAIN || errno == EINTR) {
138 SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
141 SILC_LOG_DEBUG(("Cannot read from socket: %d:%s", fd, strerror(errno)));
142 sock->sock_error = errno;
149 /* Insert the data to the buffer. */
152 sock->inbuf = silc_buffer_alloc(SILC_SOCKET_BUF_SIZE);
154 /* If the data does not fit to the buffer reallocate it */
155 if ((sock->inbuf->end - sock->inbuf->tail) < len)
156 sock->inbuf = silc_buffer_realloc(sock->inbuf, sock->inbuf->truelen +
158 silc_buffer_put_tail(sock->inbuf, buf, len);
159 silc_buffer_pull_tail(sock->inbuf, len);
161 SILC_LOG_DEBUG(("Read %d bytes", len));
163 /* Apply QoS to the read data if necessary */
165 struct timeval curtime;
166 silc_gettimeofday(&curtime);
168 /* If we have passed the rate time limit, set our new time limit,
169 and zero the rate limit. */
170 if (!silc_compare_timeval(&curtime, &sock->qos->next_limit)) {
172 sock->qos->next_limit = curtime;
173 sock->qos->cur_rate = 0;
175 sock->qos->cur_rate++;
177 /* If we are not withing rate limit apply QoS for the read data */
178 if (sock->qos->cur_rate > sock->qos->read_rate) {
179 silc_schedule_task_add(sock->qos->schedule, sock->sock,
180 silc_socket_read_qos, silc_socket_dup(sock),
181 sock->qos->limit_sec, sock->qos->limit_usec,
182 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
183 silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
185 /* Check the byte limit as well, and do not return more than allowed */
186 if (sock->inbuf->len > sock->qos->read_limit_bytes) {
187 /* Hide the rest of the data from the buffer. */
188 sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
189 silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
190 len = sock->inbuf->len;
192 /* Rate limit kicked in, do not return data yet */
196 /* Check the byte limit, and do not return more than allowed */
197 if (sock->inbuf->len > sock->qos->read_limit_bytes) {
198 silc_schedule_task_add(sock->qos->schedule, sock->sock,
199 silc_socket_read_qos, silc_socket_dup(sock),
200 sock->qos->limit_sec, sock->qos->limit_usec,
201 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
202 silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
204 /* Hide the rest of the data from the buffer. */
205 sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
206 silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
207 len = sock->inbuf->len;
215 /* Returns human readable socket error message */
217 bool silc_socket_get_error(SilcSocketConnection sock, char *error,
218 SilcUInt32 error_len)
222 if (!sock->sock_error)
225 err = strerror(sock->sock_error);
226 if (strlen(err) > error_len)
229 memset(error, 0, error_len);
230 memcpy(error, err, strlen(err));