1 /*- 2 * Copyright (c) 2009-2010 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Pawel Jakub Dawidek under sponsorship from 6 * the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/types.h> 34 #include <sys/queue.h> 35 #include <sys/socket.h> 36 37 #include <assert.h> 38 #include <errno.h> 39 #include <stdint.h> 40 41 #include "proto.h" 42 #include "proto_impl.h" 43 44 #define PROTO_CONN_MAGIC 0x907041c 45 struct proto_conn { 46 int pc_magic; 47 struct hast_proto *pc_proto; 48 void *pc_ctx; 49 int pc_side; 50 #define PROTO_SIDE_CLIENT 0 51 #define PROTO_SIDE_SERVER_LISTEN 1 52 #define PROTO_SIDE_SERVER_WORK 2 53 }; 54 55 static LIST_HEAD(, hast_proto) protos = LIST_HEAD_INITIALIZER(protos); 56 57 void 58 proto_register(struct hast_proto *proto) 59 { 60 61 LIST_INSERT_HEAD(&protos, proto, hp_next); 62 } 63 64 static int 65 proto_common_setup(const char *addr, struct proto_conn **connp, int side) 66 { 67 struct hast_proto *proto; 68 struct proto_conn *conn; 69 void *ctx; 70 int ret; 71 72 assert(side == PROTO_SIDE_CLIENT || side == PROTO_SIDE_SERVER_LISTEN); 73 74 conn = malloc(sizeof(*conn)); 75 if (conn == NULL) 76 return (-1); 77 78 LIST_FOREACH(proto, &protos, hp_next) { 79 if (side == PROTO_SIDE_CLIENT) 80 ret = proto->hp_client(addr, &ctx); 81 else /* if (side == PROTO_SIDE_SERVER_LISTEN) */ 82 ret = proto->hp_server(addr, &ctx); 83 /* 84 * ret == 0 - success 85 * ret == -1 - addr is not for this protocol 86 * ret > 0 - right protocol, but an error occured 87 */ 88 if (ret >= 0) 89 break; 90 } 91 if (proto == NULL) { 92 /* Unrecognized address. */ 93 free(conn); 94 errno = EINVAL; 95 return (-1); 96 } 97 if (ret > 0) { 98 /* An error occured. */ 99 free(conn); 100 errno = ret; 101 return (-1); 102 } 103 conn->pc_proto = proto; 104 conn->pc_ctx = ctx; 105 conn->pc_side = side; 106 conn->pc_magic = PROTO_CONN_MAGIC; 107 *connp = conn; 108 return (0); 109 } 110 111 int 112 proto_client(const char *addr, struct proto_conn **connp) 113 { 114 115 return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT)); 116 } 117 118 int 119 proto_connect(struct proto_conn *conn) 120 { 121 int ret; 122 123 assert(conn != NULL); 124 assert(conn->pc_magic == PROTO_CONN_MAGIC); 125 assert(conn->pc_side == PROTO_SIDE_CLIENT); 126 assert(conn->pc_proto != NULL); 127 128 ret = conn->pc_proto->hp_connect(conn->pc_ctx); 129 if (ret != 0) { 130 errno = ret; 131 return (-1); 132 } 133 134 return (0); 135 } 136 137 int 138 proto_server(const char *addr, struct proto_conn **connp) 139 { 140 141 return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN)); 142 } 143 144 int 145 proto_accept(struct proto_conn *conn, struct proto_conn **newconnp) 146 { 147 struct proto_conn *newconn; 148 int ret; 149 150 assert(conn != NULL); 151 assert(conn->pc_magic == PROTO_CONN_MAGIC); 152 assert(conn->pc_side == PROTO_SIDE_SERVER_LISTEN); 153 assert(conn->pc_proto != NULL); 154 155 newconn = malloc(sizeof(*newconn)); 156 if (newconn == NULL) 157 return (-1); 158 159 ret = conn->pc_proto->hp_accept(conn->pc_ctx, &newconn->pc_ctx); 160 if (ret != 0) { 161 free(newconn); 162 errno = ret; 163 return (-1); 164 } 165 166 newconn->pc_proto = conn->pc_proto; 167 newconn->pc_side = PROTO_SIDE_SERVER_WORK; 168 newconn->pc_magic = PROTO_CONN_MAGIC; 169 *newconnp = newconn; 170 171 return (0); 172 } 173 174 int 175 proto_send(struct proto_conn *conn, const void *data, size_t size) 176 { 177 int ret; 178 179 assert(conn != NULL); 180 assert(conn->pc_magic == PROTO_CONN_MAGIC); 181 assert(conn->pc_proto != NULL); 182 183 ret = conn->pc_proto->hp_send(conn->pc_ctx, data, size); 184 if (ret != 0) { 185 errno = ret; 186 return (-1); 187 } 188 return (0); 189 } 190 191 int 192 proto_recv(struct proto_conn *conn, void *data, size_t size) 193 { 194 int ret; 195 196 assert(conn != NULL); 197 assert(conn->pc_magic == PROTO_CONN_MAGIC); 198 assert(conn->pc_proto != NULL); 199 200 ret = conn->pc_proto->hp_recv(conn->pc_ctx, data, size); 201 if (ret != 0) { 202 errno = ret; 203 return (-1); 204 } 205 return (0); 206 } 207 208 int 209 proto_descriptor(const struct proto_conn *conn) 210 { 211 212 assert(conn != NULL); 213 assert(conn->pc_magic == PROTO_CONN_MAGIC); 214 assert(conn->pc_proto != NULL); 215 216 return (conn->pc_proto->hp_descriptor(conn->pc_ctx)); 217 } 218 219 bool 220 proto_address_match(const struct proto_conn *conn, const char *addr) 221 { 222 223 assert(conn != NULL); 224 assert(conn->pc_magic == PROTO_CONN_MAGIC); 225 assert(conn->pc_proto != NULL); 226 227 return (conn->pc_proto->hp_address_match(conn->pc_ctx, addr)); 228 } 229 230 void 231 proto_local_address(const struct proto_conn *conn, char *addr, size_t size) 232 { 233 234 assert(conn != NULL); 235 assert(conn->pc_magic == PROTO_CONN_MAGIC); 236 assert(conn->pc_proto != NULL); 237 238 conn->pc_proto->hp_local_address(conn->pc_ctx, addr, size); 239 } 240 241 void 242 proto_remote_address(const struct proto_conn *conn, char *addr, size_t size) 243 { 244 245 assert(conn != NULL); 246 assert(conn->pc_magic == PROTO_CONN_MAGIC); 247 assert(conn->pc_proto != NULL); 248 249 conn->pc_proto->hp_remote_address(conn->pc_ctx, addr, size); 250 } 251 252 int 253 proto_timeout(const struct proto_conn *conn, int timeout) 254 { 255 struct timeval tv; 256 int fd; 257 258 assert(conn != NULL); 259 assert(conn->pc_magic == PROTO_CONN_MAGIC); 260 assert(conn->pc_proto != NULL); 261 262 fd = proto_descriptor(conn); 263 if (fd < 0) 264 return (-1); 265 266 tv.tv_sec = timeout; 267 tv.tv_usec = 0; 268 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) 269 return (-1); 270 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) 271 return (-1); 272 273 return (0); 274 } 275 276 void 277 proto_close(struct proto_conn *conn) 278 { 279 280 assert(conn != NULL); 281 assert(conn->pc_magic == PROTO_CONN_MAGIC); 282 assert(conn->pc_proto != NULL); 283 284 conn->pc_proto->hp_close(conn->pc_ctx); 285 conn->pc_magic = 0; 286 free(conn); 287 } 288