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));