|
5 | 5 | static VALUE sym_wait_readable, sym_wait_writable;
|
6 | 6 |
|
7 | 7 | #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
|
8 |
| -static VALUE rb_cAncillaryData; |
| 8 | +VALUE rb_cAncillaryData; |
9 | 9 |
|
10 | 10 | static VALUE
|
11 | 11 | constant_to_sym(int constant, ID (*intern_const)(int))
|
@@ -270,6 +270,51 @@ ancillary_unix_rights(VALUE self)
|
270 | 270 | #define ancillary_unix_rights rb_f_notimplement
|
271 | 271 | #endif
|
272 | 272 |
|
| 273 | +/* |
| 274 | + * call-seq: |
| 275 | + * ancillarydata.credentials(local_creds_enabled: false, socket: nil) => Socket::Credentials instance |
| 276 | + * |
| 277 | + * Interprets the contents of this ancillary message as a Socket::Credentials |
| 278 | + * instance. If the ancillary data is not of type SCM_CREDS or SCM_CREDENTIALS, |
| 279 | + * an exception is raised, or if the data is malformed, an exception is raised. |
| 280 | + * |
| 281 | + * If _local_creds_enabled_ or _socket_ are passed in, they are used to resolve |
| 282 | + * any ambiguity in the format of the ancillary message data as described for |
| 283 | + * Socket::Credentials.from_ancillary_data |
| 284 | + */ |
| 285 | +static VALUE |
| 286 | +ancillary_credentials(int argc, VALUE *argv, VALUE self) |
| 287 | +{ |
| 288 | + /* Annoyingly, we need to support passing local_creds_enabled: and |
| 289 | + * socket: kwargs through to Socket::Credentials::from_ancillary_data, |
| 290 | + * if present. It's kind of verbose to do that in C. */ |
| 291 | + VALUE kwarg_hash = Qnil; |
| 292 | + ID kwarg_keys[2] = { |
| 293 | + rb_intern("local_creds_enabled"), |
| 294 | + rb_intern("socket"), |
| 295 | + }; |
| 296 | + VALUE kwarg_values[2] = { Qundef, Qundef }; |
| 297 | + |
| 298 | + rb_scan_args(argc, argv, "0:", &kwarg_hash); |
| 299 | + if (RB_TEST(kwarg_hash)) { |
| 300 | + rb_get_kwargs(kwarg_hash, kwarg_keys, 0, 2, kwarg_values); |
| 301 | + } |
| 302 | + |
| 303 | + VALUE passed_args[4]; |
| 304 | + passed_args[0] = rb_attr_get(self, rb_intern("level")); |
| 305 | + passed_args[1] = rb_attr_get(self, rb_intern("type")); |
| 306 | + passed_args[2] = rb_attr_get(self, rb_intern("data")); |
| 307 | + passed_args[3] = RB_TEST(kwarg_hash) ? kwarg_hash : rb_hash_new(); |
| 308 | + |
| 309 | + VALUE r = rb_funcallv_kw(rb_cSocketCredentials, rb_intern("from_ancillary_data"), |
| 310 | + 4, passed_args, 1); |
| 311 | + if (!RB_TEST(r)) { |
| 312 | + rb_raise(rb_eTypeError, |
| 313 | + "SCM_CREDS or SCM_CREDENTIALS ancillary data was malformed"); |
| 314 | + } |
| 315 | + return r; |
| 316 | +} |
| 317 | + |
273 | 318 | #if defined(SCM_TIMESTAMP) || defined(SCM_TIMESTAMPNS) || defined(SCM_BINTIME)
|
274 | 319 | /*
|
275 | 320 | * call-seq:
|
@@ -688,88 +733,19 @@ anc_inspect_socket_rights(int level, int type, VALUE data, VALUE ret)
|
688 | 733 | }
|
689 | 734 | #endif
|
690 | 735 |
|
691 |
| -#if defined(SCM_CREDENTIALS) /* GNU/Linux */ |
692 |
| -static int |
693 |
| -anc_inspect_passcred_credentials(int level, int type, VALUE data, VALUE ret) |
694 |
| -{ |
695 |
| - if (level == SOL_SOCKET && type == SCM_CREDENTIALS && |
696 |
| - RSTRING_LEN(data) == sizeof(struct ucred)) { |
697 |
| - struct ucred cred; |
698 |
| - memcpy(&cred, RSTRING_PTR(data), sizeof(struct ucred)); |
699 |
| - rb_str_catf(ret, " pid=%u uid=%u gid=%u", cred.pid, cred.uid, cred.gid); |
700 |
| - rb_str_cat2(ret, " (ucred)"); |
701 |
| - return 1; |
702 |
| - } |
703 |
| - else { |
704 |
| - return 0; |
705 |
| - } |
706 |
| -} |
707 |
| -#endif |
708 | 736 |
|
709 |
| -#if defined(SCM_CREDS) |
710 |
| -#define INSPECT_SCM_CREDS |
| 737 | +#if defined(SCM_CREDS) || defined(SCM_CREDENTIALS) |
711 | 738 | static int
|
712 | 739 | anc_inspect_socket_creds(int level, int type, VALUE data, VALUE ret)
|
713 | 740 | {
|
714 |
| - if (level != SOL_SOCKET && type != SCM_CREDS) |
| 741 | + VALUE cr = rb_funcall(rb_cSocketCredentials, rb_intern("from_ancillary_data"), 3, |
| 742 | + RB_INT2NUM(level), RB_INT2NUM(type), data); |
| 743 | + if (!RB_TEST(cr)) { |
715 | 744 | return 0;
|
716 |
| - |
717 |
| - /* |
718 |
| - * FreeBSD has struct cmsgcred and struct sockcred. |
719 |
| - * They use both SOL_SOCKET/SCM_CREDS in the ancillary message. |
720 |
| - * They are not ambiguous from the view of the caller |
721 |
| - * because struct sockcred is sent if and only if the caller sets LOCAL_CREDS socket option. |
722 |
| - * But inspect method doesn't know it. |
723 |
| - * So they are ambiguous from the view of inspect. |
724 |
| - * This function distinguish them by the size of the ancillary message. |
725 |
| - * This heuristics works well except when sc_ngroups == CMGROUP_MAX. |
726 |
| - */ |
727 |
| - |
728 |
| -#if defined(HAVE_TYPE_STRUCT_CMSGCRED) /* FreeBSD */ |
729 |
| - if (RSTRING_LEN(data) == sizeof(struct cmsgcred)) { |
730 |
| - struct cmsgcred cred; |
731 |
| - memcpy(&cred, RSTRING_PTR(data), sizeof(struct cmsgcred)); |
732 |
| - rb_str_catf(ret, " pid=%u", cred.cmcred_pid); |
733 |
| - rb_str_catf(ret, " uid=%u", cred.cmcred_uid); |
734 |
| - rb_str_catf(ret, " euid=%u", cred.cmcred_euid); |
735 |
| - rb_str_catf(ret, " gid=%u", cred.cmcred_gid); |
736 |
| - if (cred.cmcred_ngroups) { |
737 |
| - int i; |
738 |
| - const char *sep = " groups="; |
739 |
| - for (i = 0; i < cred.cmcred_ngroups; i++) { |
740 |
| - rb_str_catf(ret, "%s%u", sep, cred.cmcred_groups[i]); |
741 |
| - sep = ","; |
742 |
| - } |
743 |
| - } |
744 |
| - rb_str_cat2(ret, " (cmsgcred)"); |
745 |
| - return 1; |
746 |
| - } |
747 |
| -#endif |
748 |
| -#if defined(HAVE_TYPE_STRUCT_SOCKCRED) /* FreeBSD, NetBSD */ |
749 |
| - if ((size_t)RSTRING_LEN(data) >= SOCKCREDSIZE(0)) { |
750 |
| - struct sockcred cred0, *cred; |
751 |
| - memcpy(&cred0, RSTRING_PTR(data), SOCKCREDSIZE(0)); |
752 |
| - if ((size_t)RSTRING_LEN(data) == SOCKCREDSIZE(cred0.sc_ngroups)) { |
753 |
| - cred = (struct sockcred *)ALLOCA_N(char, SOCKCREDSIZE(cred0.sc_ngroups)); |
754 |
| - memcpy(cred, RSTRING_PTR(data), SOCKCREDSIZE(cred0.sc_ngroups)); |
755 |
| - rb_str_catf(ret, " uid=%u", cred->sc_uid); |
756 |
| - rb_str_catf(ret, " euid=%u", cred->sc_euid); |
757 |
| - rb_str_catf(ret, " gid=%u", cred->sc_gid); |
758 |
| - rb_str_catf(ret, " egid=%u", cred->sc_egid); |
759 |
| - if (cred0.sc_ngroups) { |
760 |
| - int i; |
761 |
| - const char *sep = " groups="; |
762 |
| - for (i = 0; i < cred0.sc_ngroups; i++) { |
763 |
| - rb_str_catf(ret, "%s%u", sep, cred->sc_groups[i]); |
764 |
| - sep = ","; |
765 |
| - } |
766 |
| - } |
767 |
| - rb_str_cat2(ret, " (sockcred)"); |
768 |
| - return 1; |
769 |
| - } |
770 | 745 | }
|
771 |
| -#endif |
772 |
| - return 0; |
| 746 | + VALUE fragment = rsock_credentials_inspect_fragment(cr); |
| 747 | + rb_str_append(ret, fragment); |
| 748 | + return 1; |
773 | 749 | }
|
774 | 750 | #endif
|
775 | 751 |
|
@@ -1030,9 +1006,9 @@ ancillary_inspect(VALUE self)
|
1030 | 1006 | case SCM_RIGHTS: inspected = anc_inspect_socket_rights(level, type, data, ret); break;
|
1031 | 1007 | # endif
|
1032 | 1008 | # if defined(SCM_CREDENTIALS) /* GNU/Linux */
|
1033 |
| - case SCM_CREDENTIALS: inspected = anc_inspect_passcred_credentials(level, type, data, ret); break; |
| 1009 | + case SCM_CREDENTIALS: inspected = anc_inspect_socket_creds(level, type, data, ret); break; |
1034 | 1010 | # endif
|
1035 |
| -# if defined(INSPECT_SCM_CREDS) /* NetBSD */ |
| 1011 | +# if defined(SCM_CREDS) /* FreeBSD, NetBSD */ |
1036 | 1012 | case SCM_CREDS: inspected = anc_inspect_socket_creds(level, type, data, ret); break;
|
1037 | 1013 | # endif
|
1038 | 1014 | }
|
@@ -1721,6 +1697,7 @@ rsock_init_ancdata(void)
|
1721 | 1697 |
|
1722 | 1698 | rb_define_singleton_method(rb_cAncillaryData, "unix_rights", ancillary_s_unix_rights, -1);
|
1723 | 1699 | rb_define_method(rb_cAncillaryData, "unix_rights", ancillary_unix_rights, 0);
|
| 1700 | + rb_define_method(rb_cAncillaryData, "credentials", ancillary_credentials, -1); |
1724 | 1701 |
|
1725 | 1702 | rb_define_method(rb_cAncillaryData, "timestamp", ancillary_timestamp, 0);
|
1726 | 1703 |
|
|
0 commit comments