ext/sctp/socket.c in sctp-socket-0.0.4 vs ext/sctp/socket.c in sctp-socket-0.0.5

- old
+ new

@@ -15,15 +15,48 @@ VALUE v_sndinfo_struct; VALUE v_adaptation_event_struct; VALUE v_partial_delivery_event_struct; VALUE v_auth_event_struct; VALUE v_sockaddr_in_struct; +VALUE v_sctp_status_struct; +VALUE v_sctp_rtoinfo_struct; +VALUE v_sctp_associnfo_struct; +VALUE v_sctp_default_send_params_struct; +#if !defined(IOV_MAX) +#if defined(_SC_IOV_MAX) +#define IOV_MAX (sysconf(_SC_IOV_MAX)) +#else +// Assume infinity, or let the syscall return with error +#define IOV_MAX INT_MAX +#endif +#endif + +#define ARY2IOVEC(iov,ary) \ + do { \ + VALUE *cur; \ + struct iovec *tmp; \ + long n; \ + cur = RARRAY_PTR(ary); \ + n = RARRAY_LEN(ary); \ + iov = tmp = alloca(sizeof(struct iovec) * n); \ + for (; --n >= 0; tmp++, cur++) { \ + if (TYPE(*cur) != T_STRING) \ + rb_raise(rb_eArgError, "must be an array of strings"); \ + tmp->iov_base = RSTRING_PTR(*cur); \ + tmp->iov_len = RSTRING_LEN(*cur); \ + } \ + } while (0) + +// TODO: Yes, I know I need to update the signature. VALUE convert_sockaddr_in_to_struct(struct sockaddr_in* addr){ - char ipbuf[16]; + char ipbuf[INET6_ADDRSTRLEN]; - inet_ntop(addr->sin_family, &(((struct sockaddr_in *)addr)->sin_addr), ipbuf, sizeof(ipbuf)); + if(addr->sin_family == AF_INET6) + inet_ntop(addr->sin_family, &(((struct sockaddr_in6 *)addr)->sin6_addr), ipbuf, sizeof(ipbuf)); + else + inet_ntop(addr->sin_family, &(((struct sockaddr_in *)addr)->sin_addr), ipbuf, sizeof(ipbuf)); return rb_struct_new(v_sockaddr_in_struct, INT2NUM(addr->sin_family), INT2NUM(ntohs(addr->sin_port)), rb_str_new2(ipbuf) @@ -314,10 +347,55 @@ sctp_freeladdrs(addrs); return v_array; } +#ifdef HAVE_SCTP_SENDV +static VALUE rsctp_sendv(VALUE self, VALUE v_messages){ + struct iovec* iov; + struct sockaddr* addrs[8]; + struct sctp_sndinfo info; + int sock_fd, num_bytes, size; + + Check_Type(v_messages, T_ARRAY); + bzero(&addrs, sizeof(addrs)); + + sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd")); + + Check_Type(v_messages, T_ARRAY); + size = RARRAY_LEN(v_messages); + + if(!size) + rb_raise(rb_eArgError, "Must contain at least one message"); + + if(size > IOV_MAX) + rb_raise(rb_eArgError, "Array size is greater than IOV_MAX"); + + ARY2IOVEC(iov, v_messages); + + info.snd_flags = SCTP_UNORDERED; + info.snd_assoc_id = NUM2INT(rb_iv_get(self, "@association_id")); + + num_bytes = sctp_sendv( + sock_fd, + iov, + size, + NULL, + 0, + &info, + sizeof(info), + SCTP_SENDV_SNDINFO, + 0 + ); + + if(num_bytes < 0) + rb_raise(rb_eSystemCallError, "sctp_sendv: %s", strerror(errno)); + + return INT2NUM(num_bytes); +} +#endif + /* * Send a message on an already-connected socket to a specific association. * * Example: * @@ -676,10 +754,11 @@ UINT2NUM(snp->sn_remote_error.sre_error), UINT2NUM(snp->sn_remote_error.sre_assoc_id), rb_ary_new4(snp->sn_remote_error.sre_length, v_temp) ); break; +#ifdef SCTP_SEND_FAILED_EVENT case SCTP_SEND_FAILED_EVENT: v_temp = ALLOCA_N(VALUE, snp->sn_send_failed_event.ssf_length); for(i = 0; i < snp->sn_send_failed_event.ssf_length; i++){ v_temp[i] = UINT2NUM(snp->sn_send_failed_event.ssf_data[i]); @@ -698,10 +777,28 @@ ), UINT2NUM(snp->sn_send_failed_event.ssf_assoc_id), rb_ary_new4(snp->sn_send_failed_event.ssf_length, v_temp) ); break; +#else + case SCTP_SEND_FAILED: + v_temp = ALLOCA_N(VALUE, snp->sn_send_failed.ssf_length); + + for(i = 0; i < snp->sn_send_failed.ssf_length; i++){ + v_temp[i] = UINT2NUM(snp->sn_send_failed.ssf_data[i]); + } + + v_notification = rb_struct_new(v_send_failed_event_struct, + UINT2NUM(snp->sn_send_failed.ssf_type), + UINT2NUM(snp->sn_send_failed.ssf_length), + UINT2NUM(snp->sn_send_failed.ssf_error), + Qnil, + UINT2NUM(snp->sn_send_failed.ssf_assoc_id), + rb_ary_new4(snp->sn_send_failed.ssf_length, v_temp) + ); + break; +#endif case SCTP_SHUTDOWN_EVENT: v_notification = rb_struct_new(v_shutdown_event_struct, UINT2NUM(snp->sn_shutdown_event.sse_type), UINT2NUM(snp->sn_shutdown_event.sse_length), UINT2NUM(snp->sn_shutdown_event.sse_assoc_id) @@ -858,13 +955,16 @@ events.sctp_association_event = 1; if(RTEST(rb_hash_aref2(v_options, "address"))) events.sctp_address_event = 1; - // Use the new version if(RTEST(rb_hash_aref2(v_options, "send_failure"))) +#ifdef HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_SEND_FAILURE_EVENT + events.sctp_send_failure_event = 1; +#else events.sctp_send_failure_event_event = 1; +#endif if(RTEST(rb_hash_aref2(v_options, "peer_error"))) events.sctp_peer_error_event = 1; if(RTEST(rb_hash_aref2(v_options, "shutdown"))) @@ -940,10 +1040,65 @@ rb_iv_set(self, "@sock_fd", INT2NUM(new_sock_fd)); return self; } +static VALUE rsctp_get_default_send_params(VALUE self){ + int sock_fd; + socklen_t size; + sctp_assoc_t assoc_id; + struct sctp_sndrcvinfo sndrcv; + + bzero(&sndrcv, sizeof(sndrcv)); + + sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd")); + assoc_id = NUM2INT(rb_iv_get(self, "@association_id")); + size = sizeof(struct sctp_sndrcvinfo); + + if(sctp_opt_info(sock_fd, assoc_id, SCTP_DEFAULT_SEND_PARAM, (void*)&sndrcv, &size) < 0) + rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno)); + + return rb_struct_new( + v_sctp_default_send_params_struct, + INT2NUM(sndrcv.sinfo_stream), + INT2NUM(sndrcv.sinfo_ssn), + INT2NUM(sndrcv.sinfo_flags), + INT2NUM(sndrcv.sinfo_ppid), + INT2NUM(sndrcv.sinfo_context), + INT2NUM(sndrcv.sinfo_timetolive), + INT2NUM(sndrcv.sinfo_tsn), + INT2NUM(sndrcv.sinfo_cumtsn), + INT2NUM(sndrcv.sinfo_assoc_id) + ); +} + +static VALUE rsctp_get_association_info(VALUE self){ + int sock_fd; + socklen_t size; + sctp_assoc_t assoc_id; + struct sctp_assocparams assoc; + + bzero(&assoc, sizeof(assoc)); + + sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd")); + assoc_id = NUM2INT(rb_iv_get(self, "@association_id")); + size = sizeof(struct sctp_assocparams); + + if(sctp_opt_info(sock_fd, assoc_id, SCTP_ASSOCINFO, (void*)&assoc, &size) < 0) + rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno)); + + return rb_struct_new( + v_sctp_associnfo_struct, + INT2NUM(assoc.sasoc_assoc_id), + INT2NUM(assoc.sasoc_asocmaxrxt), + INT2NUM(assoc.sasoc_number_peer_destinations), + INT2NUM(assoc.sasoc_peer_rwnd), + INT2NUM(assoc.sasoc_local_rwnd), + INT2NUM(assoc.sasoc_cookie_life) + ); +} + static VALUE rsctp_shutdown(int argc, VALUE* argv, VALUE self){ int how, sock_fd; VALUE v_how; sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd")); @@ -962,10 +1117,77 @@ rb_raise(rb_eSystemCallError, "shutdown: %s", strerror(errno)); return self; } +static VALUE rsctp_get_retransmission_info(VALUE self){ + int sock_fd; + socklen_t size; + sctp_assoc_t assoc_id; + struct sctp_rtoinfo rto; + + bzero(&rto, sizeof(rto)); + + sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd")); + assoc_id = NUM2INT(rb_iv_get(self, "@association_id")); + size = sizeof(struct sctp_rtoinfo); + + if(sctp_opt_info(sock_fd, assoc_id, SCTP_RTOINFO, (void*)&rto, &size) < 0) + rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno)); + + return rb_struct_new( + v_sctp_rtoinfo_struct, + INT2NUM(rto.srto_assoc_id), + INT2NUM(rto.srto_initial), + INT2NUM(rto.srto_max), + INT2NUM(rto.srto_min) + ); +} + +static VALUE rsctp_get_status(VALUE self){ + int sock_fd; + socklen_t size; + sctp_assoc_t assoc_id; + struct sctp_status status; + struct sctp_paddrinfo* spinfo; + char tmpname[INET_ADDRSTRLEN]; + + bzero(&status, sizeof(status)); + + sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd")); + assoc_id = NUM2INT(rb_iv_get(self, "@association_id")); + size = sizeof(struct sctp_status); + + if(sctp_opt_info(sock_fd, assoc_id, SCTP_STATUS, (void*)&status, &size) < 0) + rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno)); + + spinfo = &status.sstat_primary; + + if (spinfo->spinfo_address.ss_family == AF_INET6) { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)&spinfo->spinfo_address; + inet_ntop(AF_INET6, &sin6->sin6_addr, tmpname, sizeof (tmpname)); + } + else { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&spinfo->spinfo_address; + inet_ntop(AF_INET, &sin->sin_addr, tmpname, sizeof (tmpname)); + } + + return rb_struct_new(v_sctp_status_struct, + INT2NUM(status.sstat_assoc_id), + INT2NUM(status.sstat_state), + INT2NUM(status.sstat_rwnd), + INT2NUM(status.sstat_unackdata), + INT2NUM(status.sstat_penddata), + INT2NUM(status.sstat_instrms), + INT2NUM(status.sstat_outstrms), + INT2NUM(status.sstat_fragmentation_point), + rb_str_new2(tmpname) + ); +} + void Init_socket(){ mSCTP = rb_define_module("SCTP"); cSocket = rb_define_class_under(mSCTP, "Socket", rb_cObject); v_sndrcv_struct = rb_struct_define( @@ -1014,21 +1236,50 @@ v_sockaddr_in_struct = rb_struct_define( "SockAddrIn", "family", "port", "address", NULL ); + v_sctp_status_struct = rb_struct_define( + "Status", "association_id", "state", "receive_window", "unacknowledged_data", + "pending_data", "inbound_streams", "outbound_streams", "fragmentation_point", "primary", NULL + ); + + v_sctp_rtoinfo_struct = rb_struct_define( + "RetransmissionInfo", "association_id", "initial", "max", "min", NULL + ); + + v_sctp_associnfo_struct = rb_struct_define( + "AssociationInfo", "association_id", "max_retransmission_count", + "number_peer_destinations", "peer_receive_window", "local_receive_window", + "cookie_life", NULL + ); + + v_sctp_default_send_params_struct = rb_struct_define( + "DefaultSendParams", "stream", "ssn", "flags", "ppid", "context", + "ttl", "tsn", "cumtsn", "association_id", NULL + ); + rb_define_method(cSocket, "initialize", rsctp_init, -1); rb_define_method(cSocket, "bind", rsctp_bind, -1); rb_define_method(cSocket, "close", rsctp_close, 0); rb_define_method(cSocket, "connect", rsctp_connect, -1); rb_define_method(cSocket, "getpeernames", rsctp_getpeernames, 0); rb_define_method(cSocket, "getlocalnames", rsctp_getlocalnames, 0); + rb_define_method(cSocket, "get_status", rsctp_get_status, 0); + rb_define_method(cSocket, "get_default_send_params", rsctp_get_default_send_params, 0); + rb_define_method(cSocket, "get_retransmission_info", rsctp_get_retransmission_info, 0); + rb_define_method(cSocket, "get_association_info", rsctp_get_association_info, 0); rb_define_method(cSocket, "listen", rsctp_listen, -1); rb_define_method(cSocket, "peeloff!", rsctp_peeloff, 1); rb_define_method(cSocket, "recvmsg", rsctp_recvmsg, -1); rb_define_method(cSocket, "send", rsctp_send, 1); + +#ifdef HAVE_SCTP_SENDV + rb_define_method(cSocket, "sendv", rsctp_sendv, 1); +#endif + rb_define_method(cSocket, "sendmsg", rsctp_sendmsg, 1); rb_define_method(cSocket, "set_initmsg", rsctp_set_initmsg, 1); rb_define_method(cSocket, "shutdown", rsctp_shutdown, -1); rb_define_method(cSocket, "subscribe", rsctp_subscribe, 1); @@ -1036,12 +1287,12 @@ rb_define_attr(cSocket, "type", 1, 1); rb_define_attr(cSocket, "sock_fd", 1, 1); rb_define_attr(cSocket, "association_id", 1, 1); rb_define_attr(cSocket, "port", 1, 1); - /* 0.0.4: The version of this library */ - rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.4")); + /* 0.0.5: The version of this library */ + rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.5")); /* send flags */ /* Message is unordered */ rb_define_const(cSocket, "SCTP_UNORDERED", INT2NUM(SCTP_UNORDERED)); @@ -1057,6 +1308,18 @@ /* Send to all associations */ rb_define_const(cSocket, "SCTP_SENDALL", INT2NUM(SCTP_SENDALL)); rb_define_const(cSocket, "MSG_NOTIFICATION", INT2NUM(MSG_NOTIFICATION)); + + // ASSOCIATION STATES // + + rb_define_const(cSocket, "SCTP_EMPTY", INT2NUM(SCTP_EMPTY)); + rb_define_const(cSocket, "SCTP_CLOSED", INT2NUM(SCTP_CLOSED)); + rb_define_const(cSocket, "SCTP_COOKIE_WAIT", INT2NUM(SCTP_COOKIE_WAIT)); + rb_define_const(cSocket, "SCTP_COOKIE_ECHOED", INT2NUM(SCTP_COOKIE_ECHOED)); + rb_define_const(cSocket, "SCTP_ESTABLISHED", INT2NUM(SCTP_ESTABLISHED)); + rb_define_const(cSocket, "SCTP_SHUTDOWN_PENDING", INT2NUM(SCTP_SHUTDOWN_PENDING)); + rb_define_const(cSocket, "SCTP_SHUTDOWN_SENT", INT2NUM(SCTP_SHUTDOWN_SENT)); + rb_define_const(cSocket, "SCTP_SHUTDOWN_RECEIVED", INT2NUM(SCTP_SHUTDOWN_RECEIVED)); + rb_define_const(cSocket, "SCTP_SHUTDOWN_ACK_SENT", INT2NUM(SCTP_SHUTDOWN_ACK_SENT)); }