1/* $OpenBSD: roaming_common.c,v 1.10 2013/07/12 00:19:59 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 if (size == 0 || size > MAX_ROAMBUF) 79 fatal("%s: bad buffer size %lu", __func__, (u_long)size); 80 /* 81 * The buffer size can only be set once and the buffer will live 82 * as long as the session lives. 83 */ 84 if (out_buf == NULL) { 85 out_buf_size = size; 86 out_buf = xmalloc(size); 87 out_start = 0; 88 out_last = 0; 89 } 90} 91 92u_int64_t 93get_recv_bytes(void) 94{ 95 return read_bytes; 96} 97 98void 99add_recv_bytes(u_int64_t num) 100{ 101 read_bytes += num; 102} 103 104u_int64_t 105get_sent_bytes(void) 106{ 107 return write_bytes; 108} 109 110void 111roam_set_bytes(u_int64_t sent, u_int64_t recvd) 112{ 113 read_bytes = recvd; 114 write_bytes = sent; 115} 116 117static void 118buf_append(const char *buf, size_t count) 119{ 120 if (count > out_buf_size) { 121 buf += count - out_buf_size; 122 count = out_buf_size; 123 } 124 if (count < out_buf_size - out_last) { 125 memcpy(out_buf + out_last, buf, count); 126 if (out_start > out_last) 127 out_start += count; 128 out_last += count; 129 } else { 130 /* data will wrap */ 131 size_t chunk = out_buf_size - out_last; 132 memcpy(out_buf + out_last, buf, chunk); 133 memcpy(out_buf, buf + chunk, count - chunk); 134 out_last = count - chunk; 135 out_start = out_last + 1; 136 } 137} 138 139ssize_t 140roaming_write(int fd, const void *buf, size_t count, int *cont) 141{ 142 ssize_t ret; 143 144 ret = write(fd, buf, count); 145 if (ret > 0 && !resume_in_progress) { 146 write_bytes += ret; 147 if (out_buf_size > 0) 148 buf_append(buf, ret); 149 } 150 if (out_buf_size > 0 && 151 (ret == 0 || (ret == -1 && errno == EPIPE))) { 152 if (wait_for_roaming_reconnect() != 0) { 153 ret = 0; 154 *cont = 1; 155 } else { 156 ret = -1; 157 errno = EAGAIN; 158 } 159 } 160 return ret; 161} 162 163ssize_t 164roaming_read(int fd, void *buf, size_t count, int *cont) 165{ 166 ssize_t ret = read(fd, buf, count); 167 if (ret > 0) { 168 if (!resume_in_progress) { 169 read_bytes += ret; 170 } 171 } else if (out_buf_size > 0 && 172 (ret == 0 || (ret == -1 && (errno == ECONNRESET 173 || errno == ECONNABORTED || errno == ETIMEDOUT 174 || errno == EHOSTUNREACH)))) { 175 debug("roaming_read failed for %d ret=%ld errno=%d", 176 fd, (long)ret, errno); 177 ret = 0; 178 if (wait_for_roaming_reconnect() == 0) 179 *cont = 1; 180 } 181 return ret; 182} 183 184size_t 185roaming_atomicio(ssize_t(*f)(int, void*, size_t), int fd, void *buf, 186 size_t count) 187{ 188 size_t ret = atomicio(f, fd, buf, count); 189 190 if (f == vwrite && ret > 0 && !resume_in_progress) { 191 write_bytes += ret; 192 } else if (f == read && ret > 0 && !resume_in_progress) { 193 read_bytes += ret; 194 } 195 return ret; 196} 197 198void 199resend_bytes(int fd, u_int64_t *offset) 200{ 201 size_t available, needed; 202 203 if (out_start < out_last) 204 available = out_last - out_start; 205 else 206 available = out_buf_size; 207 needed = write_bytes - *offset; 208 debug3("resend_bytes: resend %lu bytes from %llu", 209 (unsigned long)needed, (unsigned long long)*offset); 210 if (needed > available) 211 fatal("Needed to resend more data than in the cache"); 212 if (out_last < needed) { 213 int chunkend = needed - out_last; 214 atomicio(vwrite, fd, out_buf + out_buf_size - chunkend, 215 chunkend); 216 atomicio(vwrite, fd, out_buf, out_last); 217 } else { 218 atomicio(vwrite, fd, out_buf + (out_last - needed), needed); 219 } 220} 221 222/* 223 * Caclulate a new key after a reconnect 224 */ 225void 226calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge) 227{ 228 const EVP_MD *md = EVP_sha1(); 229 EVP_MD_CTX ctx; 230 u_char hash[EVP_MAX_MD_SIZE]; 231 Buffer b; 232 233 buffer_init(&b); 234 buffer_put_int64(&b, *key); 235 buffer_put_int64(&b, cookie); 236 buffer_put_int64(&b, challenge); 237 238 EVP_DigestInit(&ctx, md); 239 EVP_DigestUpdate(&ctx, buffer_ptr(&b), buffer_len(&b)); 240 EVP_DigestFinal(&ctx, hash, NULL); 241 242 buffer_clear(&b); 243 buffer_append(&b, hash, EVP_MD_size(md)); 244 *key = buffer_get_int64(&b); 245 buffer_free(&b); 246} 247