[socket] Add zx_socket_info_t

After this CL, we can use zx_object_get_info to get the options and
buffer sizes for the socket.

Test: socket_buffer_test
Change-Id: I26e08c11b07ab482bbceff46b37566f5b72cded5
diff --git a/docs/syscalls/object_get_info.md b/docs/syscalls/object_get_info.md
index c9fc6bd..007cfbc 100644
--- a/docs/syscalls/object_get_info.md
+++ b/docs/syscalls/object_get_info.md
@@ -279,7 +279,6 @@
 } zx_info_cpu_stats_t;
 ```
 
-
 ### ZX_INFO_VMAR
 
 *handle* type: **VM Address Region**
@@ -299,6 +298,35 @@
 This returns a single *zx_info_vmar_t* that describes the range of address
 space that the VMAR occupies.
 
+### ZX_INFO_SOCKET
+
+*handle* type: **Socket**
+
+*buffer* type: **zx_info_socket_t[1]**
+
+```
+typedef struct zx_info_socket {
+    // The options passed to zx_socket_create().
+    uint32_t options;
+
+    // The value of ZX_PROP_SOCKET_RX_BUF_MAX.
+    size_t rx_buf_max;
+
+    // The value of ZX_PROP_SOCKET_RX_BUF_SIZE.
+    size_t rx_buf_size;
+
+    // The value of ZX_PROP_SOCKET_TX_BUF_MAX.
+    //
+    // Will be zero if the peer endpoint is closed.
+    size_t tx_buf_max;
+
+    // The value of ZX_PROP_SOCKET_TX_BUF_SIZE.
+    //
+    // Will be zero if the peer endpoint is closed.
+    size_t tx_buf_size;
+} zx_info_socket_t;
+```
+
 ### ZX_INFO_JOB_CHILDREN
 
 *handle* type: **Job**
@@ -545,6 +573,7 @@
 *   *ZX_RSRC_KIND_IOPORT*
 *   *ZX_RSRC_KIND_IRQ*
 *   *ZX_RSRC_KIND_HYPERVISOR*
+
 ### ZX_INFO_BTI
 
 *handle* type: **Bus Transaction Initiator**
diff --git a/kernel/object/include/object/socket_dispatcher.h b/kernel/object/include/object/socket_dispatcher.h
index bff2156..c82a28e 100644
--- a/kernel/object/include/object/socket_dispatcher.h
+++ b/kernel/object/include/object/socket_dispatcher.h
@@ -57,6 +57,8 @@
     size_t TransmitBufferMax() const;
     size_t TransmitBufferSize() const;
 
+    void GetInfo(zx_info_socket_t* info) const;
+
     zx_status_t CheckShareable(SocketDispatcher* to_send);
 
     struct ControlMsg {
diff --git a/kernel/object/socket_dispatcher.cpp b/kernel/object/socket_dispatcher.cpp
index 12dc4e9..47168c8 100644
--- a/kernel/object/socket_dispatcher.cpp
+++ b/kernel/object/socket_dispatcher.cpp
@@ -440,3 +440,15 @@
     Guard<fbl::Mutex> guard{get_lock()};
     return peer_ ? peer_->data_.size() : 0;
 }
+
+void SocketDispatcher::GetInfo(zx_info_socket_t* info) const TA_NO_THREAD_SAFETY_ANALYSIS {
+    canary_.Assert();
+    Guard<fbl::Mutex> guard{get_lock()};
+    *info = zx_info_socket_t{
+        .options = flags_,
+        .rx_buf_max = data_.max_size(),
+        .rx_buf_size = data_.size(),
+        .tx_buf_max = peer_ ? peer_->data_.max_size() : 0,
+        .tx_buf_size = peer_ ? peer_->data_.size() : 0,
+    };
+}
diff --git a/kernel/syscalls/object.cpp b/kernel/syscalls/object.cpp
index 5a8d0f0..c9d28b3 100644
--- a/kernel/syscalls/object.cpp
+++ b/kernel/syscalls/object.cpp
@@ -589,6 +589,19 @@
             _buffer, buffer_size, _actual, _avail, &info, sizeof(info));
     }
 
+    case ZX_INFO_SOCKET: {
+        fbl::RefPtr<SocketDispatcher> socket;
+        auto status = up->GetDispatcherWithRights(handle, ZX_RIGHT_INSPECT, &socket);
+        if (status != ZX_OK)
+            return status;
+
+        zx_info_socket_t info = {};
+        socket->GetInfo(&info);
+
+        return single_record_result(
+            _buffer, buffer_size, _actual, _avail, &info, sizeof(info));
+    }
+
     default:
         return ZX_ERR_NOT_SUPPORTED;
     }
diff --git a/system/public/zircon/syscalls/object.h b/system/public/zircon/syscalls/object.h
index c90ff6c..7ef8df6 100644
--- a/system/public/zircon/syscalls/object.h
+++ b/system/public/zircon/syscalls/object.h
@@ -33,7 +33,7 @@
     ZX_INFO_HANDLE_COUNT               = 19, // zx_info_handle_count_t[1]
     ZX_INFO_BTI                        = 20, // zx_info_bti_t[1]
     ZX_INFO_PROCESS_HANDLE_STATS       = 21, // zx_info_process_handle_stats_t[1]
-    ZX_INFO_LAST
+    ZX_INFO_SOCKET                     = 22, // zx_info_socket_t[1]
 } zx_object_info_topic_t;
 
 typedef uint32_t zx_obj_props_t;
@@ -154,6 +154,26 @@
     uint64_t aspace_size;
 } zx_info_bti_t;
 
+typedef struct zx_info_socket {
+    // The options passed to zx_socket_create().
+    uint32_t options;
+
+    // The value of ZX_PROP_SOCKET_RX_BUF_MAX.
+    size_t rx_buf_max;
+
+    // The value of ZX_PROP_SOCKET_RX_BUF_SIZE.
+    size_t rx_buf_size;
+
+    // The value of ZX_PROP_SOCKET_TX_BUF_MAX.
+    //
+    // Will be zero if the peer endpoint is closed.
+    size_t tx_buf_max;
+
+    // The value of ZX_PROP_SOCKET_TX_BUF_SIZE.
+    //
+    // Will be zero if the peer endpoint is closed.
+    size_t tx_buf_size;
+} zx_info_socket_t;
 
 // Types and values used by ZX_INFO_PROCESS_MAPS.
 
diff --git a/system/utest/property/property.c b/system/utest/property/property.c
index 54ea2d1..b501569 100644
--- a/system/utest/property/property.c
+++ b/system/utest/property/property.c
@@ -255,6 +255,24 @@
     ASSERT_EQ(zx_socket_write(sockets[1], 0, buf, sizeof(buf), &actual), ZX_OK, "");
     EXPECT_EQ(actual, sizeof(buf), "");
 
+    zx_info_socket_t info;
+
+    memset(&info, 0, sizeof(info));
+    ASSERT_EQ(zx_object_get_info(sockets[0], ZX_INFO_SOCKET, &info, sizeof(info), NULL, NULL), ZX_OK, "");
+    EXPECT_EQ(info.options, 0u, "");
+    EXPECT_GT(info.rx_buf_max, 0u, "");
+    EXPECT_EQ(info.rx_buf_size, sizeof(buf), "");
+    EXPECT_GT(info.tx_buf_max, 0u, "");
+    EXPECT_EQ(info.tx_buf_size, 0u, "");
+
+    memset(&info, 0, sizeof(info));
+    ASSERT_EQ(zx_object_get_info(sockets[1], ZX_INFO_SOCKET, &info, sizeof(info), NULL, NULL), ZX_OK, "");
+    EXPECT_EQ(info.options, 0u, "");
+    EXPECT_GT(info.rx_buf_max, 0u, "");
+    EXPECT_EQ(info.rx_buf_size, 0u, "");
+    EXPECT_GT(info.tx_buf_max, 0u, "");
+    EXPECT_EQ(info.tx_buf_size, sizeof(buf), "");
+
     ASSERT_EQ(zx_object_get_property(sockets[0], ZX_PROP_SOCKET_RX_BUF_SIZE, &value, sizeof(value)),
               ZX_OK, "");
     EXPECT_EQ(value, sizeof(buf), "");
@@ -270,6 +288,18 @@
     ASSERT_EQ(zx_object_get_property(sockets[1], ZX_PROP_SOCKET_TX_BUF_MAX, &value, sizeof(value)),
               ZX_OK, "");
     EXPECT_EQ(value, 0u, "");
+    zx_handle_close(sockets[1]);
+
+    ASSERT_EQ(zx_socket_create(ZX_SOCKET_DATAGRAM, &sockets[0], &sockets[1]), ZX_OK, "");
+
+    memset(&info, 0, sizeof(info));
+    ASSERT_EQ(zx_object_get_info(sockets[0], ZX_INFO_SOCKET, &info, sizeof(info), NULL, NULL), ZX_OK, "");
+    EXPECT_EQ(info.options, ZX_SOCKET_DATAGRAM, "");
+    EXPECT_GT(info.rx_buf_max, 0u, "");
+    EXPECT_EQ(info.rx_buf_size, 0u, "");
+    EXPECT_GT(info.tx_buf_max, 0u, "");
+    EXPECT_EQ(info.tx_buf_size, 0u, "");
+    zx_handle_close_many(sockets, 2);
 
     END_TEST;
 }