1 /* $OpenBSD: roaming_common.c,v 1.8 2010/01/12 00:59:29 djm Exp $ */ 2 /* 3 * Copyright (c) 2004-2009 AppGate Network Security AB 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "includes.h" 19 20 #include <sys/types.h> 21 #include <sys/socket.h> 22 #include <sys/uio.h> 23 24 #include <errno.h> 25 #ifdef HAVE_INTTYPES_H 26 #include <inttypes.h> 27 #endif 28 #include <stdarg.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 #include "atomicio.h" 33 #include "log.h" 34 #include "packet.h" 35 #include "xmalloc.h" 36 #include "cipher.h" 37 #include "buffer.h" 38 #include "roaming.h" 39 40 static size_t out_buf_size = 0; 41 static char *out_buf = NULL; 42 static size_t out_start; 43 static size_t out_last; 44 45 static u_int64_t write_bytes = 0; 46 static u_int64_t read_bytes = 0; 47 48 int roaming_enabled = 0; 49 int resume_in_progress = 0; 50 51 int 52 get_snd_buf_size() 53 { 54 int fd = packet_get_connection_out(); 55 int optval; 56 socklen_t optvallen = sizeof(optval); 57 58 if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optvallen) != 0) 59 optval = DEFAULT_ROAMBUF; 60 return optval; 61 } 62 63 int 64 get_recv_buf_size() 65 { 66 int fd = packet_get_connection_in(); 67 int optval; 68 socklen_t optvallen = sizeof(optval); 69 70 if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &optvallen) != 0) 71 optval = DEFAULT_ROAMBUF; 72 return optval; 73 } 74 75 void 76 set_out_buffer_size(size_t size) 77 { 78 /* 79 * The buffer size can only be set once and the buffer will live 80 * as long as the session lives. 81 */ 82 if (out_buf == NULL) { 83 out_buf_size = size; 84 out_buf = xmalloc(size); 85 out_start = 0; 86 out_last = 0; 87 } 88 } 89 90 u_int64_t 91 get_recv_bytes(void) 92 { 93 return read_bytes; 94 } 95 96 void 97 add_recv_bytes(u_int64_t num) 98 { 99 read_bytes += num; 100 } 101 102 u_int64_t 103 get_sent_bytes(void) 104 { 105 return write_bytes; 106 } 107 108 void 109 roam_set_bytes(u_int64_t sent, u_int64_t recvd) 110 { 111 read_bytes = recvd; 112 write_bytes = sent; 113 } 114 115 static void 116 buf_append(const char *buf, size_t count) 117 { 118 if (count > out_buf_size) { 119 buf += count - out_buf_size; 120 count = out_buf_size; 121 } 122 if (count < out_buf_size - out_last) { 123 memcpy(out_buf + out_last, buf, count); 124 if (out_start > out_last) 125 out_start += count; 126 out_last += count; 127 } else { 128 /* data will wrap */ 129 size_t chunk = out_buf_size - out_last; 130 memcpy(out_buf + out_last, buf, chunk); 131 memcpy(out_buf, buf + chunk, count - chunk); 132 out_last = count - chunk; 133 out_start = out_last + 1; 134 } 135 } 136 137 ssize_t 138 roaming_write(int fd, const void *buf, size_t count, int *cont) 139 { 140 ssize_t ret; 141 142 ret = write(fd, buf, count); 143 if (ret > 0 && !resume_in_progress) { 144 write_bytes += ret; 145 if (out_buf_size > 0) 146 buf_append(buf, ret); 147 } 148 if (out_buf_size > 0 && 149 (ret == 0 || (ret == -1 && errno == EPIPE))) { 150 if (wait_for_roaming_reconnect() != 0) { 151 ret = 0; 152 *cont = 1; 153 } else { 154 ret = -1; 155 errno = EAGAIN; 156 } 157 } 158 return ret; 159 } 160 161 ssize_t 162 roaming_read(int fd, void *buf, size_t count, int *cont) 163 { 164 ssize_t ret = read(fd, buf, count); 165 if (ret > 0) { 166 if (!resume_in_progress) { 167 read_bytes += ret; 168 } 169 } else if (out_buf_size > 0 && 170 (ret == 0 || (ret == -1 && (errno == ECONNRESET 171 || errno == ECONNABORTED || errno == ETIMEDOUT 172 || errno == EHOSTUNREACH)))) { 173 debug("roaming_read failed for %d ret=%ld errno=%d", 174 fd, (long)ret, errno); 175 ret = 0; 176 if (wait_for_roaming_reconnect() == 0) 177 *cont = 1; 178 } 179 return ret; 180 } 181 182 size_t 183 roaming_atomicio(ssize_t(*f)(int, void*, size_t), int fd, void *buf, 184 size_t count) 185 { 186 size_t ret = atomicio(f, fd, buf, count); 187 188 if (f == vwrite && ret > 0 && !resume_in_progress) { 189 write_bytes += ret; 190 } else if (f == read && ret > 0 && !resume_in_progress) { 191 read_bytes += ret; 192 } 193 return ret; 194 } 195 196 void 197 resend_bytes(int fd, u_int64_t *offset) 198 { 199 size_t available, needed; 200 201 if (out_start < out_last) 202 available = out_last - out_start; 203 else 204 available = out_buf_size; 205 needed = write_bytes - *offset; 206 debug3("resend_bytes: resend %lu bytes from %llu", 207 (unsigned long)needed, (unsigned long long)*offset); 208 if (needed > available) 209 fatal("Needed to resend more data than in the cache"); 210 if (out_last < needed) { 211 int chunkend = needed - out_last; 212 atomicio(vwrite, fd, out_buf + out_buf_size - chunkend, 213 chunkend); 214 atomicio(vwrite, fd, out_buf, out_last); 215 } else { 216 atomicio(vwrite, fd, out_buf + (out_last - needed), needed); 217 } 218 } 219 220 /* 221 * Caclulate a new key after a reconnect 222 */ 223 void 224 calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge) 225 { 226 const EVP_MD *md = EVP_sha1(); 227 EVP_MD_CTX ctx; 228 char hash[EVP_MAX_MD_SIZE]; 229 Buffer b; 230 231 buffer_init(&b); 232 buffer_put_int64(&b, *key); 233 buffer_put_int64(&b, cookie); 234 buffer_put_int64(&b, challenge); 235 236 EVP_DigestInit(&ctx, md); 237 EVP_DigestUpdate(&ctx, buffer_ptr(&b), buffer_len(&b)); 238 EVP_DigestFinal(&ctx, hash, NULL); 239 240 buffer_clear(&b); 241 buffer_append(&b, hash, EVP_MD_size(md)); 242 *key = buffer_get_int64(&b); 243 buffer_free(&b); 244 } 245