ext/sctp/socket.c in sctp-socket-0.1.2 vs ext/sctp/socket.c in sctp-socket-0.1.3

- old
+ new

@@ -333,15 +333,32 @@ * call-seq: * SCTP::Socket#bindx(options) * * Bind a subset of IP addresses associated with the host system on the * given port, or a port assigned by the operating system if none is provided. + * You may bind a maximum of eight IP addresses. * * Note that you can both add or remove an address to or from the socket * using the SCTP_BINDX_ADD_ADDR (default) or SCTP_BINDX_REM_ADDR constants, * respectively. * + * Possible options: + * + * * addresses - Array of IP addresses to bind to. If none are specified then + * INADDR_ANY is used. + * + * * port - The port to bind to. If none is specified then 0 is used, i.e. the + * OS will select one for you. + * + * * flags - Flags to pass to the underlying sctp_bindx function. In practice + * there are only two options: BINDX_ADD_ADDR and BINDX_REM_ADDR. By + * default a flag of BINDX_ADD_ADDR is assumed. + * + * * reuse_addr - If set to true, then the SO_REUSEADDR flag will be applied to + * the socket before the bind call. This will allow other sockets to reuse + * the addresses that are currently bound to the socket. + * * Example: * * socket = SCTP::Socket.new * * # Bind 2 addresses @@ -355,12 +372,12 @@ * * Returns the port that it was bound to. */ static VALUE rsctp_bindx(int argc, VALUE* argv, VALUE self){ struct sockaddr_in addrs[8]; - int i, fileno, num_ip, flags, domain, port; - VALUE v_addresses, v_port, v_flags, v_address, v_options; + int i, fileno, num_ip, flags, domain, port, on; + VALUE v_addresses, v_port, v_flags, v_address, v_reuse_addr, v_options; rb_scan_args(argc, argv, "01", &v_options); bzero(&addrs, sizeof(addrs)); @@ -368,10 +385,11 @@ v_options = rb_hash_new(); v_addresses = rb_hash_aref2(v_options, "addresses"); v_flags = rb_hash_aref2(v_options, "flags"); v_port = rb_hash_aref2(v_options, "port"); + v_reuse_addr = rb_hash_aref2(v_options, "reuse_addr"); if(NIL_P(v_port)) port = 0; else port = NUM2INT(v_port); @@ -384,14 +402,17 @@ if(NIL_P(v_addresses)) num_ip = 1; else num_ip = (int)RARRAY_LEN(v_addresses); + if(num_ip > 8) + rb_raise(rb_eArgError, "too many IP addresses to bind, maximum is eight"); + domain = NUM2INT(rb_iv_get(self, "@domain")); fileno = NUM2INT(rb_iv_get(self, "@fileno")); - if(num_ip > 1){ + if(!NIL_P(v_addresses)){ for(i = 0; i < num_ip; i++){ v_address = RARRAY_PTR(v_addresses)[i]; addrs[i].sin_family = domain; addrs[i].sin_port = htons(port); addrs[i].sin_addr.s_addr = inet_addr(StringValueCStr(v_address)); @@ -407,10 +428,16 @@ #ifdef BSD addrs[0].sin_len = sizeof(struct sockaddr_in); #endif } + if(v_reuse_addr == Qtrue){ + on = 1; + if(setsockopt(fileno, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno)); + } + if(sctp_bindx(fileno, (struct sockaddr *) addrs, num_ip, flags) != 0) rb_raise(rb_eSystemCallError, "sctp_bindx: %s", strerror(errno)); if(port == 0){ struct sockaddr_in sin; @@ -495,19 +522,50 @@ * call-seq: * SCTP::Socket#close * * Close the socket. You should always do this. * + * By default the underlying close operation is non-blocking. This means that the + * bound IP addresses may not be available right away after closing. You may + * optionally control this behavior with the +linger+ option. + * + * + * * linger - If present, this should be set to a numeric value, in seconds. + * The value will cause the close operation to block for that number of + * seconds, after which it will return (i.e. return to non-blocking). + * * Example: * * socket = SCTP::Socket.new - * socket.close + * socket.close # or + * socket.close(linger: 5) */ -static VALUE rsctp_close(VALUE self){ - VALUE v_fileno = rb_iv_get(self, "@fileno"); +static VALUE rsctp_close(int argc, VALUE* argv, VALUE self){ + VALUE v_options, v_linger; + int fileno; - if(close(NUM2INT(v_fileno))) + rb_scan_args(argc, argv, "01", &v_options); + + if(NIL_P(v_options)) + v_options = rb_hash_new(); + + Check_Type(v_options, T_HASH); + + v_linger = rb_hash_aref2(v_options, "linger"); + + fileno = NUM2INT(rb_iv_get(self, "@fileno")); + + if(!NIL_P(v_linger)){ + struct linger lin; + lin.l_onoff = 1; + lin.l_linger = NUM2INT(v_linger); + + if(setsockopt(fileno, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0) + rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno)); + } + + if(close(fileno)) rb_raise(rb_eSystemCallError, "close: %s", strerror(errno)); return self; } @@ -693,10 +751,11 @@ if(size > IOV_MAX) rb_raise(rb_eArgError, "Array size is greater than IOV_MAX"); // TODO: Make this configurable + spa.sendv_flags = SCTP_SEND_SNDINFO_VALID; spa.sendv_sndinfo.snd_flags = SCTP_UNORDERED; spa.sendv_sndinfo.snd_assoc_id = NUM2INT(rb_iv_get(self, "@association_id")); if(!NIL_P(v_addresses)){ int i, port, domain; @@ -2296,11 +2355,11 @@ rb_define_method(cSocket, "initialize", rsctp_init, -1); rb_define_method(cSocket, "autoclose=", rsctp_set_autoclose, 1); rb_define_method(cSocket, "bindx", rsctp_bindx, -1); - rb_define_method(cSocket, "close", rsctp_close, 0); + rb_define_method(cSocket, "close", rsctp_close, -1); rb_define_method(cSocket, "connectx", rsctp_connectx, -1); rb_define_method(cSocket, "delete_shared_key", rsctp_delete_shared_key, -1); rb_define_method(cSocket, "disable_fragments=", rsctp_disable_fragments, 1); rb_define_method(cSocket, "enable_auth_support", rsctp_enable_auth_support, -1); rb_define_method(cSocket, "getpeernames", rsctp_getpeernames, -1); @@ -2342,11 +2401,11 @@ rb_define_attr(cSocket, "type", 1, 1); rb_define_attr(cSocket, "fileno", 1, 1); rb_define_attr(cSocket, "association_id", 1, 1); rb_define_attr(cSocket, "port", 1, 1); - /* 0.1.2: The version of this library */ - rb_define_const(cSocket, "VERSION", rb_str_new2("0.1.2")); + /* 0.1.3: The version of this library */ + rb_define_const(cSocket, "VERSION", rb_str_new2("0.1.3")); /* send flags */ /* Message is unordered */ rb_define_const(cSocket, "SCTP_UNORDERED", INT2NUM(SCTP_UNORDERED));