Annotation of embedaddon/trafshow/session.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 1999-2003 Rinet Corp., Novosibirsk, Russia
! 3: *
! 4: * Redistribution and use in source forms, with and without modification,
! 5: * are permitted provided that this entire comment appears intact.
! 6: *
! 7: * THIS SOURCE CODE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
! 8: */
! 9:
! 10: #ifdef HAVE_CONFIG_H
! 11: #include <config.h>
! 12: #endif
! 13:
! 14: #include <sys/types.h>
! 15: #include <sys/socket.h>
! 16: #include <netinet/in.h>
! 17: #include <fcntl.h>
! 18: #include <stdlib.h>
! 19: #include <string.h>
! 20: #include <unistd.h>
! 21: #include <errno.h>
! 22:
! 23: #include "session.h"
! 24: #include "events.h" /* just for tv_sub() */
! 25:
! 26: #define dprintf(x) /* nope */
! 27:
! 28: #ifndef BUF_SIZE
! 29: #define BUF_SIZE 8192
! 30: #endif
! 31: #ifndef MAX_STR_LEN
! 32: #define MAX_STR_LEN 1500 /* must be vastly smaller then BUF_SIZE */
! 33: #endif
! 34:
! 35: #ifdef O_NONBLOCK
! 36: #define ASYNC_MODE O_NONBLOCK
! 37: #elif O_NDELAY
! 38: #define ASYNC_MODE O_NDELAY
! 39: #elif FNDELAY
! 40: #define ASYNC_MODE FNDELAY
! 41: #elif O_ASYNC
! 42: #define ASYNC_MODE O_ASYNC
! 43: #elif
! 44: #error the fcntl argument to turn ON/OFF non-blocking I/O is unknown
! 45: #endif
! 46:
! 47: static int session_read(SESSION *sd);
! 48:
! 49: static SESSION *first_session = 0; /* first network session in table */
! 50:
! 51: typedef struct session_binder_ent {
! 52: void (*notify)(void *arg); /* call it before free */
! 53: void *arg;
! 54: struct session_binder_ent *next;
! 55: } SESSION_BINDER;
! 56:
! 57:
! 58: SESSION *
! 59: session_open(sock, peer, type)
! 60: int sock;
! 61: const struct sockaddr *peer;
! 62: SessionType type;
! 63: {
! 64: SESSION *sd, *prev = 0, *next = 0;
! 65: static u_long sid = 0;
! 66:
! 67: /*
! 68: * Search for first empty or last session slot.
! 69: */
! 70: for (sd = first_session; sd; sd = sd->next) {
! 71: if (!sd->sid) {
! 72: next = sd->next;
! 73: break;
! 74: }
! 75: prev = sd;
! 76: }
! 77: if (!sd && (sd = (SESSION *)malloc(sizeof(SESSION))) == 0)
! 78: return 0;
! 79: memset(sd, 0, sizeof(SESSION));
! 80:
! 81: if (++sid == 0) sid++; /* prevent 0 sid */
! 82: sd->sid = sid;
! 83: sd->sock = sock;
! 84: if (peer)
! 85: memcpy(&sd->peer, peer, sizeof(struct sockaddr));
! 86: else memset(&sd->peer, 0, sizeof(sd->peer));
! 87: memset(&sd->from, 0, sizeof(sd->from));
! 88:
! 89: sd->type = type;
! 90:
! 91: /* make chain */
! 92: if (next) sd->next = next;
! 93: else if (prev) prev->next = sd;
! 94: if (!first_session) first_session = sd;
! 95:
! 96: if (session_start(sd) < 0) {
! 97: sd->sid = 0; /* this slot may be recycled later */
! 98: sd = 0;
! 99: }
! 100: return sd;
! 101: }
! 102:
! 103: int
! 104: session_start(sd)
! 105: SESSION *sd;
! 106: {
! 107: int af;
! 108:
! 109: /* sanity check */
! 110: if (!sd) {
! 111: errno = EINVAL;
! 112: return -1;
! 113: }
! 114: errno = EBADF;
! 115: if (sd->sock != -1 &&
! 116: (sd->type == PlainFile || socket_peer((struct sockaddr *)&sd->peer, sd->sock) != -1)) {
! 117: /* already connected for example by accept() */
! 118:
! 119: socket_nonblock(sd->sock, 0);
! 120: if (sd->type == TextStream)
! 121: socket_keepalive(sd->sock, 1);
! 122: return 0;
! 123: }
! 124:
! 125: af = sd->peer.ss_family;
! 126: if (!af) af = AF_INET; /* by default */
! 127:
! 128: if (errno == EBADF || errno == ENOTSOCK) {
! 129: switch (sd->type) {
! 130: case PlainFile:
! 131: sd->sock = -1;
! 132: errno = EINVAL;
! 133: break;
! 134: case TextStream:
! 135: sd->sock = socket(af, SOCK_STREAM, 0);
! 136: break;
! 137: case DataSequence:
! 138: sd->sock = socket(af, SOCK_DGRAM, 0);
! 139: break;
! 140: /* XXX other session types would be added here */
! 141: }
! 142: if (sd->sock == -1)
! 143: return -1;
! 144:
! 145: errno = ENOTCONN;
! 146: }
! 147: if (errno == ENOTCONN) {
! 148: /*
! 149: * Make socket `connected' for any type, so error on this
! 150: * socket will be returned asynchronously without timing out.
! 151: */
! 152: socket_nonblock(sd->sock, 1);
! 153:
! 154: if (!sd->peer.ss_family) {
! 155: errno = 0;
! 156: return 0;
! 157: }
! 158:
! 159: if (connect(sd->sock, (struct sockaddr *)&sd->peer, sizeof(struct sockaddr)) != -1 ||
! 160: errno == EINPROGRESS)
! 161: return 0;
! 162: }
! 163: /* prevent lost of unused socket */
! 164: session_stop(sd);
! 165:
! 166: return -1;
! 167: }
! 168:
! 169: int
! 170: session_sock(sd)
! 171: SESSION *sd;
! 172: {
! 173: return (sd ? sd->sock : -1);
! 174: }
! 175:
! 176: unsigned
! 177: session_settimeout(sd, timeout)
! 178: SESSION *sd;
! 179: unsigned timeout;
! 180: {
! 181: unsigned prev;
! 182:
! 183: if (!sd || !sd->sid) return 0;
! 184:
! 185: prev = sd->timeout;
! 186: sd->timeout = timeout;
! 187:
! 188: if (sd->timeout < 1)
! 189: timerclear(&sd->expire);
! 190:
! 191: return prev;
! 192: }
! 193:
! 194: void
! 195: session_setcallback(sd, connected, read_error, read_data)
! 196: SESSION *sd;
! 197: void (*connected)(SESSION *sd);
! 198: void (*read_error)(SESSION *sd, int error);
! 199: void (*read_data)(SESSION *sd, const unsigned char *data, int len);
! 200: {
! 201: if (sd && sd->sid) {
! 202: if (connected && sd->type == TextStream) {
! 203: sd->connected = connected;
! 204: }
! 205: if (read_error)
! 206: sd->read_error = read_error;
! 207: if (read_data)
! 208: sd->read_data = read_data;
! 209: }
! 210: }
! 211:
! 212: void
! 213: session_setcookie(sd, cookie)
! 214: SESSION *sd;
! 215: const void *cookie;
! 216: {
! 217: if (sd && sd->sid) sd->cookie = cookie;
! 218: }
! 219:
! 220: const void *
! 221: session_cookie(sd)
! 222: SESSION *sd;
! 223: {
! 224: return ((sd && sd->sid) ? sd->cookie : 0);
! 225: }
! 226:
! 227: void
! 228: session_stop(sd)
! 229: SESSION *sd;
! 230: {
! 231: if (!sd) return;
! 232:
! 233: if (sd->sock != -1) {
! 234: close(sd->sock);
! 235: sd->sock = -1;
! 236: }
! 237: if (sd->buf) {
! 238: free(sd->buf);
! 239: sd->buf = 0;
! 240: }
! 241: timerclear(&sd->expire);
! 242: }
! 243:
! 244: int
! 245: session_idle(sd)
! 246: SESSION *sd;
! 247: {
! 248: if (!sd || !sd->sid) return -1;
! 249: return (sd->sock != -1 ? 0 : 1);
! 250: }
! 251:
! 252: int
! 253: session_bind(sd, notify, arg)
! 254: SESSION *sd;
! 255: void (*notify)(void *arg);
! 256: void *arg;
! 257: {
! 258: SESSION_BINDER *curr, *last = 0;
! 259:
! 260: if (!sd || !notify || !arg) {
! 261: errno = EINVAL;
! 262: return -1;
! 263: }
! 264: /* prevent dups and find last */
! 265: for (curr = sd->sb; curr; curr = curr->next) {
! 266: if (curr->notify == notify && curr->arg == arg)
! 267: return 0;
! 268: last = curr;
! 269: }
! 270: if ((curr = (SESSION_BINDER *)malloc(sizeof(SESSION_BINDER))) == 0)
! 271: return -1;
! 272: curr->notify = notify;
! 273: curr->arg = arg;
! 274: curr->next = 0;
! 275: if (last)
! 276: last->next = curr;
! 277: else sd->sb = curr;
! 278: return 0;
! 279: }
! 280:
! 281: void
! 282: session_unbind(sd, notify, arg)
! 283: SESSION *sd;
! 284: void (*notify)(void *arg);
! 285: void *arg;
! 286: {
! 287: SESSION_BINDER *curr, *prev, *next;
! 288:
! 289: curr = (sd ? sd->sb : 0);
! 290: prev = 0;
! 291: while (curr) {
! 292: if ((!notify && !arg) ||
! 293: (curr->notify == notify && curr->arg == arg)) {
! 294: next = curr->next;
! 295: if (prev)
! 296: prev->next = next;
! 297: else sd->sb = next;
! 298: free(curr);
! 299: curr = next;
! 300: } else {
! 301: prev = curr;
! 302: curr = curr->next;
! 303: }
! 304: }
! 305: }
! 306:
! 307: /*
! 308: * This function free all memory only when free_sd = 0 else it just reset
! 309: * session id and does not free memory. The mean of this behaving is to
! 310: * reuse/recycle session slots without new malloc (avoiding it overhead).
! 311: */
! 312: void
! 313: session_free(free_sd)
! 314: SESSION *free_sd;
! 315: {
! 316: SESSION *sd, *prev, *next;
! 317: SESSION_BINDER *sb;
! 318:
! 319: sd = first_session;
! 320: prev = next = 0;
! 321: while (sd) {
! 322: if (!free_sd || sd == free_sd) {
! 323: if (!free_sd) {
! 324: next = sd->next;
! 325: if (prev)
! 326: prev->next = next;
! 327: else first_session = next;
! 328: }
! 329:
! 330: for (sb = sd->sb; sb; sb = sb->next) {
! 331: if (sb->notify && sb->arg)
! 332: (*sb->notify)(sb->arg);
! 333: }
! 334: session_stop(sd);
! 335: session_unbind(sd, 0, 0); /* to free all */
! 336:
! 337: if (!free_sd) {
! 338: free(sd);
! 339: sd = next;
! 340: continue;
! 341: }
! 342: sd->sid = 0; /* this slot may be recycled later */
! 343: }
! 344: prev = sd;
! 345: sd = sd->next;
! 346: }
! 347: }
! 348:
! 349: SESSION *
! 350: session_find(peer, type)
! 351: const struct sockaddr *peer;
! 352: SessionType type;
! 353: {
! 354: SESSION *sd;
! 355:
! 356: /* sanity check */
! 357: if (!peer) return 0;
! 358:
! 359: for (sd = first_session; sd; sd = sd->next) {
! 360: if (!sd->sid || sd->type != type ||
! 361: sd->peer.ss_family != peer->sa_family)
! 362: continue;
! 363:
! 364: if (peer->sa_family == AF_INET) {
! 365: struct sockaddr_in *sin = (struct sockaddr_in *)&sd->peer;
! 366: if (sin->sin_port == ((struct sockaddr_in *)peer)->sin_port &&
! 367: !memcmp(&sin->sin_addr,
! 368: &((struct sockaddr_in *)peer)->sin_addr,
! 369: sizeof(sin->sin_addr)))
! 370: return sd;
! 371: }
! 372: #ifdef INET6
! 373: else if (peer->sa_family == AF_INET6) {
! 374: struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&sd->peer;
! 375: if (sin->sin6_port == ((struct sockaddr_in6 *)peer)->sin6_port &&
! 376: !memcmp(&sin->sin6_addr,
! 377: &((struct sockaddr_in6 *)peer)->sin6_addr,
! 378: sizeof(sin->sin6_addr)))
! 379: return sd;
! 380: }
! 381: #endif
! 382: }
! 383: return 0;
! 384: }
! 385:
! 386: int
! 387: session_send(sd, data, len)
! 388: SESSION *sd;
! 389: const unsigned char *data;
! 390: int len;
! 391: {
! 392: int wlen = 0;
! 393:
! 394: if (!sd || len < 0) {
! 395: errno = EINVAL;
! 396: return -1;
! 397: }
! 398: if (!sd->sid || sd->sock == -1) {
! 399: errno = ENOTCONN;
! 400: return -1;
! 401: }
! 402: if (data) {
! 403: if (sd->type == PlainFile) {
! 404: if (len) wlen = write(sd->sock, data, len);
! 405:
! 406: } else if (sd->type == TextStream) {
! 407: char buf[BUF_SIZE];
! 408:
! 409: if (!sd->peer.ss_family) {
! 410: errno = ENOTCONN;
! 411: return -1;
! 412: }
! 413: if (len > sizeof(buf)-2) len = sizeof(buf)-2;
! 414: if (len) memcpy(buf, data, len);
! 415: if (!len || buf[len-1] != '\n') {
! 416: buf[len++] = '\r';
! 417: buf[len++] = '\n';
! 418: }
! 419: wlen = write(sd->sock, buf, len);
! 420:
! 421: } else if (sd->type == DataSequence) {
! 422:
! 423: if (!sd->peer.ss_family) {
! 424: errno = ENOTCONN;
! 425: return -1;
! 426: }
! 427: if (len) wlen = send(sd->sock, data, len, 0);
! 428:
! 429: } else { /* XXX other session types must be added here */
! 430: wlen = -1;
! 431: errno = ESOCKTNOSUPPORT;
! 432: }
! 433: }
! 434: if (wlen == -1) {
! 435: if (errno == EAGAIN || errno == EINPROGRESS) {
! 436: errno = 0;
! 437: wlen = 0;
! 438: } else if (sd->read_error) {
! 439: (*sd->read_error)(sd, errno);
! 440: return wlen;
! 441: }
! 442: }
! 443: if (sd->timeout > 0) {
! 444: gettimeofday(&sd->expire, 0);
! 445: sd->expire.tv_sec += sd->timeout;
! 446: }
! 447: return wlen;
! 448: }
! 449:
! 450: static int
! 451: session_read(sd)
! 452: SESSION *sd;
! 453: {
! 454: int rlen = 0, rest = 0;
! 455: char *cp, *line, buf[BUF_SIZE];
! 456:
! 457: if (!sd) {
! 458: errno = EINVAL;
! 459: return -1;
! 460: }
! 461: if (!sd->sid || sd->sock == -1) {
! 462: errno = ENOTCONN;
! 463: return -1;
! 464: }
! 465: buf[0] = '\0';
! 466:
! 467: if (sd->type == PlainFile) {
! 468: rlen = read(sd->sock, buf, sizeof(buf));
! 469:
! 470: } else if (sd->type == TextStream) {
! 471: if (sd->buf) { /* previous line was truncated */
! 472: rest = strlen(strcpy(buf, sd->buf));
! 473: free(sd->buf);
! 474: sd->buf = 0;
! 475: }
! 476: rlen = read(sd->sock, &buf[rest], (sizeof(buf)-1) - rest);
! 477:
! 478: } else if (sd->type == DataSequence) {
! 479: struct sockaddr from;
! 480: socklen_t slen = sizeof(from);
! 481:
! 482: rlen = recvfrom(sd->sock, buf, sizeof(buf), 0, &from, &slen);
! 483: if (rlen != -1) {
! 484: /* just for sanity */
! 485: if (slen < sizeof(struct sockaddr_in) ||
! 486: slen > sizeof(struct sockaddr))
! 487: return 0; /* should not happen */
! 488:
! 489: if (sd->peer.ss_family &&
! 490: sd->peer.ss_family != from.sa_family)
! 491: return 0; /* bad family */
! 492:
! 493: /* save packet from */
! 494: memcpy(&sd->from, &from, slen);
! 495: } else memset(&sd->from, 0, sizeof(sd->from));
! 496:
! 497: } else { /* XXX other session types must be added here */
! 498: errno = ESOCKTNOSUPPORT;
! 499: return -1;
! 500: }
! 501:
! 502: if (rlen < 1) {
! 503: if (!rlen || !errno)
! 504: errno = ECONNRESET;
! 505: return -1;
! 506: }
! 507:
! 508: if (sd->type == PlainFile || sd->type == DataSequence) {
! 509: if (!sd->sid || sd->sock == -1)
! 510: return 0;
! 511: if (sd->read_data)
! 512: (*sd->read_data)(sd, (u_char *)buf, rlen);
! 513:
! 514: } else { /* TextStream */
! 515: buf[rest + rlen] = '\0';
! 516: for (cp = buf; (line = strchr(cp, '\n')) != 0; cp = line) {
! 517: if (line > cp && line[-1] == '\r') line[-1] = '\0';
! 518: *line++ = '\0';
! 519: if (!sd->sid || sd->sock == -1)
! 520: return 0;
! 521: if (sd->read_data) {
! 522: rest = strlen(cp);
! 523: if (rest > MAX_STR_LEN) {
! 524: errno = EMSGSIZE;
! 525: return -1;
! 526: }
! 527: (*sd->read_data)(sd, (u_char *)cp, rest);
! 528: }
! 529: }
! 530: if (cp && *cp) { /* truncated line, save it for next read */
! 531: if (strlen(cp) > MAX_STR_LEN) {
! 532: errno = EMSGSIZE;
! 533: return -1;
! 534: }
! 535: sd->buf = strdup(cp);
! 536: }
! 537: }
! 538: return rlen;
! 539: }
! 540:
! 541: int
! 542: session_select(nfds, readfds, writefds, timeout, block)
! 543: int *nfds;
! 544: fd_set *readfds, *writefds;
! 545: struct timeval *timeout;
! 546: int *block;
! 547: {
! 548: SESSION *sd;
! 549: struct timeval earliest, now;
! 550: int active = 0, pending = 0;
! 551:
! 552: timerclear(&earliest);
! 553:
! 554: /*
! 555: * For each request outstanding, add it's socket to the readfds,
! 556: * and if it is the earliest timeout to expire, mark it as lowest.
! 557: */
! 558: for (sd = first_session; sd; sd = sd->next) {
! 559: if (!sd->sid || sd->sock == -1) {
! 560: if (sd->sock != -1) /* lost session? free socket */
! 561: session_stop(sd);
! 562: continue;
! 563: }
! 564:
! 565: active++;
! 566: if (sd->sock + 1 > *nfds)
! 567: *nfds = sd->sock + 1;
! 568:
! 569: if (!sd->connected) {
! 570: FD_SET(sd->sock, readfds);
! 571:
! 572: dprintf(("session_select: sock %d set for read", sd->sock));
! 573: } else {
! 574: FD_SET(sd->sock, writefds);
! 575:
! 576: dprintf(("session_select: sock %d set for write", sd->sock));
! 577: }
! 578:
! 579: if (timerisset(&sd->expire)) {
! 580: pending++;
! 581: if (!timerisset(&earliest) ||
! 582: timercmp(&sd->expire, &earliest, <))
! 583: earliest = sd->expire;
! 584: }
! 585: }
! 586:
! 587: /*dprintf(("session_select: active=%d pending=%d", active, pending));*/
! 588:
! 589: if (!pending)
! 590: return active;
! 591:
! 592: /*
! 593: * Transforms earliest from an absolute time into a delta time, the
! 594: * time left until the select should timeout.
! 595: */
! 596: gettimeofday(&now, 0);
! 597: tv_sub(&earliest, &now);
! 598:
! 599: /* if it was blocking before or our delta time is less, reset timeout */
! 600: if (*block || timercmp(&earliest, timeout, <)) {
! 601: *timeout = earliest;
! 602: *block = 0;
! 603: }
! 604: return active;
! 605: }
! 606:
! 607: /*
! 608: * Checks to see if any of the fd's set in the readfds belong to a session.
! 609: */
! 610: void
! 611: session_operate(readfds, writefds)
! 612: fd_set *readfds, *writefds;
! 613: {
! 614: SESSION *sd;
! 615: int try_conn, error;
! 616:
! 617: for (sd = first_session; sd; sd = sd->next) {
! 618: if (!sd->sid || sd->sock == -1)
! 619: continue;
! 620:
! 621: try_conn = (sd->connected != 0);
! 622:
! 623: if (!try_conn && FD_ISSET(sd->sock, readfds)) {
! 624:
! 625: dprintf(("session_operate: sock %d ready to read", sd->sock));
! 626:
! 627: if (sd->type == PlainFile)
! 628: error = 0;
! 629: else error = socket_error(sd->sock);
! 630:
! 631: if (!error && session_read(sd) < 0)
! 632: error = errno;
! 633: if (error && sd->sid && sd->read_error)
! 634: (*sd->read_error)(sd, error);
! 635: }
! 636:
! 637: if (try_conn && FD_ISSET(sd->sock, writefds)) {
! 638:
! 639: dprintf(("session_operate: sock %d ready to write", sd->sock));
! 640:
! 641: error = socket_error(sd->sock);
! 642: if (!error) {
! 643: socket_peer((struct sockaddr *)&sd->peer, sd->sock);
! 644: socket_nonblock(sd->sock, 0);
! 645: if (sd->type == TextStream)
! 646: socket_keepalive(sd->sock, 1);
! 647: if (sd->sid && sd->connected)
! 648: (*sd->connected)(sd);
! 649: sd->connected = 0; /* fire a shot only once! */
! 650: } else if (sd->sid && sd->read_error)
! 651: (*sd->read_error)(sd, error);
! 652: }
! 653: }
! 654: }
! 655:
! 656: /*
! 657: * Checks to see if any of the sessions have an outstanding request
! 658: * that has timed out.
! 659: */
! 660: void
! 661: session_timeout()
! 662: {
! 663: SESSION *sd;
! 664: struct timeval now;
! 665:
! 666: gettimeofday(&now, 0);
! 667:
! 668: for (sd = first_session; sd; sd = sd->next) {
! 669: if (!sd->sid || sd->sock == -1)
! 670: continue;
! 671:
! 672: if (timerisset(&sd->expire) && timercmp(&sd->expire, &now, <)) {
! 673: if (sd->read_error) (*sd->read_error)(sd, ETIMEDOUT);
! 674: }
! 675: }
! 676: }
! 677:
! 678: /*
! 679: * Return session peer pointer.
! 680: */
! 681: const struct sockaddr *
! 682: session_peer(sd)
! 683: SESSION *sd;
! 684: {
! 685: return ((sd && sd->peer.ss_family) ? (struct sockaddr *)&sd->peer : 0);
! 686: }
! 687:
! 688: /*
! 689: * Return session from pointer.
! 690: */
! 691: const struct sockaddr *
! 692: session_from(sd)
! 693: SESSION *sd;
! 694: {
! 695: return ((sd && sd->from.ss_family) ? (struct sockaddr *)&sd->from : 0);
! 696: }
! 697:
! 698: /*
! 699: * Return connected socket peer ip address and port.
! 700: */
! 701: int
! 702: socket_peer(peer, sock)
! 703: struct sockaddr *peer;
! 704: int sock;
! 705: {
! 706: socklen_t arglen;
! 707:
! 708: if (!peer) {
! 709: errno = EINVAL;
! 710: return -1;
! 711: }
! 712: arglen = sizeof(struct sockaddr);
! 713: return getpeername(sock, peer, &arglen);
! 714: }
! 715:
! 716: /*
! 717: * Return socket name ip address and port.
! 718: */
! 719: int
! 720: socket_name(name, sock)
! 721: struct sockaddr *name;
! 722: int sock;
! 723: {
! 724: socklen_t arglen;
! 725:
! 726: if (!name) {
! 727: errno = EINVAL;
! 728: return -1;
! 729: }
! 730: arglen = sizeof(*name);
! 731: return getsockname(sock, name, &arglen);
! 732: }
! 733:
! 734: /*
! 735: * Return socket error (like errno) in the session or 0 if no errors.
! 736: */
! 737: int
! 738: socket_error(sock)
! 739: int sock;
! 740: {
! 741: int argbuf;
! 742: socklen_t arglen;
! 743: struct sockaddr peer;
! 744:
! 745: arglen = sizeof(argbuf);
! 746: if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &argbuf, &arglen) < 0)
! 747: return errno;
! 748: if (argbuf)
! 749: return argbuf;
! 750:
! 751: arglen = sizeof(argbuf);
! 752: if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &argbuf, &arglen) < 0)
! 753: return errno;
! 754: if (argbuf == SOCK_STREAM) {
! 755: arglen = sizeof(peer);
! 756: if (getpeername(sock, &peer, &arglen) < 0)
! 757: return errno;
! 758: }
! 759: return 0;
! 760: }
! 761:
! 762: /*
! 763: * Make socket blocked or non-blocked for sync/async I/O.
! 764: */
! 765: int
! 766: socket_nonblock(sock, on)
! 767: int sock;
! 768: int on; /* boolean */
! 769: {
! 770: int mode;
! 771: int prev; /* boolean */
! 772:
! 773: /* get current value of I/O mode */
! 774: if ((mode = fcntl(sock, F_GETFL, 0)) < 0)
! 775: return -1;
! 776:
! 777: prev = (mode & ASYNC_MODE) != 0;
! 778: if (on != prev) {
! 779: if (on) mode |= ASYNC_MODE;
! 780: else mode &= ~ASYNC_MODE;
! 781: if (fcntl(sock, F_SETFL, mode))
! 782: return -1;
! 783: }
! 784: return prev;
! 785: }
! 786:
! 787: int
! 788: socket_keepalive(sock, on)
! 789: int sock, on;
! 790: {
! 791: #ifdef SO_KEEPALIVE
! 792: int curr = 0;
! 793: socklen_t slen = sizeof(curr);
! 794: if (getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &curr, &slen) < 0)
! 795: return -1;
! 796:
! 797: curr = (curr != 0);
! 798: if (on != curr) {
! 799: if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0)
! 800: return -1;
! 801: }
! 802: return 0;
! 803: #else
! 804: errno = ESOCKTNOSUPPORT;
! 805: return -1;
! 806: #endif
! 807: }
! 808:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>