1/* $OpenBSD: roaming_common.c,v 1.12 2014/01/09 23:20:00 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#include "digest.h" 40 41static size_t out_buf_size = 0; 42static char *out_buf = NULL; 43static size_t out_start; 44static size_t out_last; 45 46static u_int64_t write_bytes = 0; 47static u_int64_t read_bytes = 0; 48 49int roaming_enabled = 0; 50int resume_in_progress = 0; 51 52int 53get_snd_buf_size(void) 54{ 55 int fd = packet_get_connection_out(); 56 int optval; 57 socklen_t optvallen = sizeof(optval); 58 59 if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optvallen) != 0) 60 optval = DEFAULT_ROAMBUF; 61 return optval; 62} 63 64int 65get_recv_buf_size(void) 66{ 67 int fd = packet_get_connection_in(); 68 int optval; 69 socklen_t optvallen = sizeof(optval); 70 71 if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &optvallen) != 0) 72 optval = DEFAULT_ROAMBUF; 73 return optval; 74} 75 76void 77set_out_buffer_size(size_t size) 78{ 79 if (size == 0 || size > MAX_ROAMBUF) 80 fatal("%s: bad buffer size %lu", __func__, (u_long)size); 81 /* 82 * The buffer size can only be set once and the buffer will live 83 * as long as the session lives. 84 */ 85 if (out_buf == NULL) { 86 out_buf_size = size; 87 out_buf = xmalloc(size); 88 out_start = 0; 89 out_last = 0; 90 } 91} 92 93u_int64_t 94get_recv_bytes(void) 95{ 96 return read_bytes; 97} 98 99void 100add_recv_bytes(u_int64_t num) 101{ 102 read_bytes += num; 103} 104 105u_int64_t 106get_sent_bytes(void) 107{ 108 return write_bytes; 109} 110 111void 112roam_set_bytes(u_int64_t sent, u_int64_t recvd) 113{ 114 read_bytes = recvd; 115 write_bytes = sent; 116} 117 118static void 119buf_append(const char *buf, size_t count) 120{ 121 if (count > out_buf_size) { 122 buf += count - out_buf_size; 123 count = out_buf_size; 124 } 125 if (count < out_buf_size - out_last) { 126 memcpy(out_buf + out_last, buf, count); 127 if (out_start > out_last) 128 out_start += count; 129 out_last += count; 130 } else { 131 /* data will wrap */ 132 size_t chunk = out_buf_size - out_last; 133 memcpy(out_buf + out_last, buf, chunk); 134 memcpy(out_buf, buf + chunk, count - chunk); 135 out_last = count - chunk; 136 out_start = out_last + 1; 137 } 138} 139 140ssize_t 141roaming_write(int fd, const void *buf, size_t count, int *cont) 142{ 143 ssize_t ret; 144 145 ret = write(fd, buf, count); 146 if (ret > 0 && !resume_in_progress) { 147 write_bytes += ret; 148 if (out_buf_size > 0) 149 buf_append(buf, ret); 150 } 151 if (out_buf_size > 0 && 152 (ret == 0 || (ret == -1 && errno == EPIPE))) { 153 if (wait_for_roaming_reconnect() != 0) { 154 ret = 0; 155 *cont = 1; 156 } else { 157 ret = -1; 158 errno = EAGAIN; 159 } 160 } 161 return ret; 162} 163 164ssize_t 165roaming_read(int fd, void *buf, size_t count, int *cont) 166{ 167 ssize_t ret = read(fd, buf, count); 168 if (ret > 0) { 169 if (!resume_in_progress) { 170 read_bytes += ret; 171 } 172 } else if (out_buf_size > 0 && 173 (ret == 0 || (ret == -1 && (errno == ECONNRESET 174 || errno == ECONNABORTED || errno == ETIMEDOUT 175 || errno == EHOSTUNREACH)))) { 176 debug("roaming_read failed for %d ret=%ld errno=%d", 177 fd, (long)ret, errno); 178 ret = 0; 179 if (wait_for_roaming_reconnect() == 0) 180 *cont = 1; 181 } 182 return ret; 183} 184 185size_t 186roaming_atomicio(ssize_t(*f)(int, void*, size_t), int fd, void *buf, 187 size_t count) 188{ 189 size_t ret = atomicio(f, fd, buf, count); 190 191 if (f == vwrite && ret > 0 && !resume_in_progress) { 192 write_bytes += ret; 193 } else if (f == read && ret > 0 && !resume_in_progress) { 194 read_bytes += ret; 195 } 196 return ret; 197} 198 199void 200resend_bytes(int fd, u_int64_t *offset) 201{ 202 size_t available, needed; 203 204 if (out_start < out_last) 205 available = out_last - out_start; 206 else 207 available = out_buf_size; 208 needed = write_bytes - *offset; 209 debug3("resend_bytes: resend %lu bytes from %llu", 210 (unsigned long)needed, (unsigned long long)*offset); 211 if (needed > available) 212 fatal("Needed to resend more data than in the cache"); 213 if (out_last < needed) { 214 int chunkend = needed - out_last; 215 atomicio(vwrite, fd, out_buf + out_buf_size - chunkend, 216 chunkend); 217 atomicio(vwrite, fd, out_buf, out_last); 218 } else { 219 atomicio(vwrite, fd, out_buf + (out_last - needed), needed); 220 } 221} 222 223/* 224 * Caclulate a new key after a reconnect 225 */ 226void 227calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge) 228{ 229 u_char hash[SSH_DIGEST_MAX_LENGTH]; 230 Buffer b; 231 232 buffer_init(&b); 233 buffer_put_int64(&b, *key); 234 buffer_put_int64(&b, cookie); 235 buffer_put_int64(&b, challenge); 236 237 if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, hash, sizeof(hash)) != 0) 238 fatal("%s: digest_buffer failed", __func__); 239 240 buffer_clear(&b); 241 buffer_append(&b, hash, ssh_digest_bytes(SSH_DIGEST_SHA1)); 242 *key = buffer_get_int64(&b); 243 buffer_free(&b); 244} 245