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 40static size_t out_buf_size = 0; 41static char *out_buf = NULL; 42static size_t out_start; 43static size_t out_last; 44 45static u_int64_t write_bytes = 0; 46static u_int64_t read_bytes = 0; 47 48int roaming_enabled = 0; 49int resume_in_progress = 0; 50 51int 52get_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 63int 64get_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 75void 76set_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 90u_int64_t 91get_recv_bytes(void) 92{ 93 return read_bytes; 94} 95 96void 97add_recv_bytes(u_int64_t num) 98{ 99 read_bytes += num; 100} 101 102u_int64_t 103get_sent_bytes(void) 104{ 105 return write_bytes; 106} 107 108void 109roam_set_bytes(u_int64_t sent, u_int64_t recvd) 110{ 111 read_bytes = recvd; 112 write_bytes = sent; 113} 114 115static void 116buf_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 137ssize_t 138roaming_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 161ssize_t 162roaming_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 182size_t 183roaming_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 196void 197resend_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 */ 223void 224calculate_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