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

- old
+ new

@@ -14,11 +14,24 @@ VALUE v_shutdown_event_struct; 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 convert_sockaddr_in_to_struct(struct sockaddr_in* addr){ + char ipbuf[16]; + + 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) + ); +} + // Helper function to get a hash value via string or symbol. VALUE rb_hash_aref2(VALUE v_hash, const char* key){ VALUE v_key, v_val; v_key = rb_str_new2(key); @@ -33,12 +46,19 @@ /* * Create and return a new SCTP::Socket instance. You may optionally pass in * a domain (aka family) value and socket type. By default these are AF_INET * and SOCK_SEQPACKET, respectively. * + * There are only two supported families: SOCK_SEQPACKET for the creation + * of a one-to-many socket, and SOCK_STREAM for the creation of a + * one-to-one socket. + * * Example: * + * require 'socket' + * require 'sctp/socket' + * * socket1 = SCTP::Socket.new * socket2 = SCTP::Socket.new(Socket::AF_INET, Socket::SOCK_STREAM) */ static VALUE rsctp_init(int argc, VALUE* argv, VALUE self){ int sock_fd; @@ -200,11 +220,11 @@ sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd")); if(sctp_connectx(sock_fd, (struct sockaddr *) addrs, num_ip, &assoc) < 0) rb_raise(rb_eSystemCallError, "sctp_connectx: %s", strerror(errno)); - rb_iv_set(self, "@assocation_id", INT2NUM(assoc)); + rb_iv_set(self, "@association_id", INT2NUM(assoc)); return self; } /* @@ -295,10 +315,103 @@ return v_array; } /* + * Send a message on an already-connected socket to a specific association. + * + * Example: + * + * socket = SCTP::Socket.new + * socket.connect(:port => 42000, :addresses => ['10.0.4.5', '10.0.5.5']) + * + * socket.send(:message => "Hello World") + * socket.send(:message => "Hello World", :association_id => 37) + * + */ +static VALUE rsctp_send(VALUE self, VALUE v_options){ + uint16_t stream; + uint32_t ppid, send_flags, ctrl_flags, ttl, context; + ssize_t num_bytes; + int sock_fd; + sctp_assoc_t assoc_id; + struct sctp_sndrcvinfo info; + VALUE v_msg, v_stream, v_ppid, v_context, v_send_flags, v_ctrl_flags, v_ttl, v_assoc_id; + + Check_Type(v_options, T_HASH); + + v_msg = rb_hash_aref2(v_options, "message"); + v_stream = rb_hash_aref2(v_options, "stream"); + v_ppid = rb_hash_aref2(v_options, "ppid"); + v_context = rb_hash_aref2(v_options, "context"); + v_send_flags = rb_hash_aref2(v_options, "send_flags"); + v_ctrl_flags = rb_hash_aref2(v_options, "control_flags"); + v_ttl = rb_hash_aref2(v_options, "ttl"); + v_assoc_id = rb_hash_aref2(v_options, "association_id"); + + if(NIL_P(v_stream)) + stream = 0; + else + stream = NUM2INT(v_stream); + + if(NIL_P(v_send_flags)) + send_flags = 0; + else + send_flags = NUM2INT(v_send_flags); + + if(NIL_P(v_ctrl_flags)) + ctrl_flags = 0; + else + ctrl_flags = NUM2INT(v_ctrl_flags); + + if(NIL_P(v_ttl)){ + ttl = 0; + } + else{ + ttl = NUM2INT(v_ttl); + send_flags |= SCTP_PR_SCTP_TTL; + } + + if(NIL_P(v_ppid)) + ppid = 0; + else + ppid = NUM2INT(v_ppid); + + if(NIL_P(v_context)) + context = 0; + else + context = NUM2INT(v_context); + + if(NIL_P(v_assoc_id)) + assoc_id = NUM2INT(rb_iv_get(self, "@association_id")); + else + assoc_id = NUM2INT(v_assoc_id); + + info.sinfo_stream = stream; + info.sinfo_flags = send_flags; + info.sinfo_ppid = ppid; + info.sinfo_context = context; + info.sinfo_timetolive = ttl; + info.sinfo_assoc_id = assoc_id; + + sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd")); + + num_bytes = sctp_send( + sock_fd, + StringValueCStr(v_msg), + RSTRING_LEN(v_msg), + &info, + ctrl_flags + ); + + if(num_bytes < 0) + rb_raise(rb_eSystemCallError, "sctp_send: %s", strerror(errno)); + + return INT2NUM(num_bytes); +} + +/* * Transmit a message to an SCTP endpoint. The following hash of options * is permitted: * * :message -> The message to send to the endpoint. Mandatory. * :stream -> The SCTP stream number you wish to send the message on. @@ -349,16 +462,19 @@ stream = NUM2INT(v_stream); if(NIL_P(v_flags)) flags = 0; else - flags = NUM2INT(v_stream); + flags = NUM2INT(v_flags); - if(NIL_P(v_ttl)) + if(NIL_P(v_ttl)){ ttl = 0; - else + } + else{ ttl = NUM2INT(v_ttl); + flags |= SCTP_PR_SCTP_TTL; + } if(NIL_P(v_ppid)) ppid = 0; else ppid = NUM2INT(v_ppid); @@ -632,11 +748,12 @@ UINT2NUM(sndrcvinfo.sinfo_flags), UINT2NUM(sndrcvinfo.sinfo_ppid), UINT2NUM(sndrcvinfo.sinfo_context), UINT2NUM(sndrcvinfo.sinfo_timetolive), UINT2NUM(sndrcvinfo.sinfo_assoc_id), - v_notification + v_notification, + convert_sockaddr_in_to_struct(&clientaddr) ); } /* * Set the initial parameters used by the socket when sending out the INIT message. @@ -691,11 +808,11 @@ * Subscribe to various notification types, which will generate additional * data that the socket may receive. The possible notification types are * as follows: * * :association - * - A change has occurred to an assocation, either a new one has begun or an existing one has end. + * - A change has occurred to an association, either a new one has begun or an existing one has end. * * :address * - The state of one of the peer's addresses has experienced a change. * * :send_failure @@ -851,11 +968,11 @@ mSCTP = rb_define_module("SCTP"); cSocket = rb_define_class_under(mSCTP, "Socket", rb_cObject); v_sndrcv_struct = rb_struct_define( "SendReceiveInfo", "message", "stream", "flags", - "ppid", "context", "ttl", "association_id", "notification", NULL + "ppid", "context", "ttl", "association_id", "notification", "client", NULL ); v_assoc_change_struct = rb_struct_define( "AssocChange", "type", "length", "state", "error", "outbound_streams", "inbound_streams", "association_id", "info", NULL @@ -893,20 +1010,25 @@ v_auth_event_struct = rb_struct_define( "AuthEvent", "type", "length", "key_number", "indication", "association_id", NULL ); + v_sockaddr_in_struct = rb_struct_define( + "SockAddrIn", "family", "port", "address", 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, "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); 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); @@ -914,8 +1036,27 @@ 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.2: The version of this library */ - rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.3")); + /* 0.0.4: The version of this library */ + rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.4")); + + /* send flags */ + + /* Message is unordered */ + rb_define_const(cSocket, "SCTP_UNORDERED", INT2NUM(SCTP_UNORDERED)); + + /* Override the primary address */ + rb_define_const(cSocket, "SCTP_ADDR_OVER", INT2NUM(SCTP_ADDR_OVER)); + + /* Send an ABORT to peer */ + rb_define_const(cSocket, "SCTP_ABORT", INT2NUM(SCTP_ABORT)); + + /* Start a shutdown procedure */ + rb_define_const(cSocket, "SCTP_EOF", INT2NUM(SCTP_EOF)); + + /* Send to all associations */ + rb_define_const(cSocket, "SCTP_SENDALL", INT2NUM(SCTP_SENDALL)); + + rb_define_const(cSocket, "MSG_NOTIFICATION", INT2NUM(MSG_NOTIFICATION)); }