Omr: Create omrsock port library in OMR

Created on 4 Jul 2019  Â·  14Comments  Â·  Source: eclipse/omr

The Eclipse OpenJ9 project has a port library implementation of network socket communication. As I understand the code may be a bit stale in places because it hasn't been used much.
The proposal is to take this code, brush it off and move it in the port library of OMR so that other projects that consume omr can take advantage of it.

backlog port

All 14 comments

Attn: @pdbain-ibm , @charliegracie

Unfavourable circumstance: Some of j9sock implementations outdated or contains some stale code. We will redesign/rewrite/clean up the code and make it part of OMR implementations.

OMR Common Data Structures:

1.

// The Plan for OMR
typedef struct omraddrinfo_struct {
        void *addr_info;
        int length;
} omraddrinfo_struct;
// j9sock
typedef struct j9addrinfo_struct {
        void *addr_info;
        int length;
#if defined(WIN32)
        int flags;
#endif
} j9addrinfo_struct;



md5-ae55741360ba97f4fb1e13561d422386



// The Plan for OMR
typedef struct hostent omrhostent;



md5-04aed1addb090b515f906fa87be5b9dc



// j9sock
typedef struct hostent OSHOSTENT;



md5-37ff359a5ae7252fa5c7f5bc197bc28d



// The Plan for OMR
#if defined(WIN32)
typedef SOCKET omrsocket;
#else
typedef int omrsocket;
#endif



md5-04aed1addb090b515f906fa87be5b9dc



// j9sock
#if defined(WIN32)
typedef SOCKET OSSOCKET;        /* as returned by socket() */
#else /* !WIN32 */
typedef int OSSOCKET;       /* as returned by socket() */
#endif /* !WIN32 */



md5-91cc4c414640d98f4c86dfa6723dd156



// The Plan for OMR ***Need help to decide where to put these data structures
// OMR unix_include
typedef struct sockaddr omrsockaddr;
typedef struct sockaddr_in omrsockaddr_in; /* for IPv4 addresses*/
typedef struct sockaddr_in6 omrsockaddr_in6; /* for IPv6 addresses*/
typedef struct sockaddr_storage omrsockaddr_storage; /* Big enough storage for IPv4 or IPv6 addresses */
typedef struct addrinfo omraddrinfo; /* addrinfo structure for both IPv4 and IPv6 */

// OMR win32_include
typedef SOCKADDR omrsockaddr; /* for IPv4 */
typedef SOCKADDR_IN omrsockaddr_in; /* for IPv4 addresses*/
typedef SOCKADDR_IN6 omrsockaddr_in6;  /* for IPv6 addresses*/
typedef struct sockaddr_storage omrsockaddr_storage; /* Big enough storage for IPv4 or IPv6 addresses */
typedef struct addrinfoW omraddrinfo;  /* addrinfo structure – Unicode for both IPv4 and IPv6 */



md5-04aed1addb090b515f906fa87be5b9dc



// j9sock
#if defined(WIN32)
typedef SOCKADDR_IN OSSOCKADDR; /* as used by bind() and friends */
typedef struct sockaddr_storage OSSOCKADDR_STORAGE; /* IPv6 */
#else
typedef struct sockaddr_in OSSOCKADDR;  /* as used by bind() and friends */
#if !defined(J9ZTPF)  /* z/TPF doesn't have IPv6 */
typedef struct sockaddr_storage OSSOCKADDR_STORAGE; /* IPv6 */
#endif /* !defined(J9ZTPF) */
#endif /* !WIN32 */

// j9sock unix_include
typedef struct sockaddr OSADDR;
typedef struct sockaddr_in6 OSSOCKADDR_IN6;  /* IPv6 */
typedef struct addrinfo OSADDRINFO;  /* IPv6 */

// j9sock win32_include
typedef SOCKADDR OSADDR;
typedef struct addrinfoW OSADDRINFO;  /* IPv6 - Unicode */
typedef SOCKADDR_IN6 OSSOCKADDR_IN6;  /* IPv6 */



md5-11f0fa029a371fec80777c7fde54d136



// j9sock
#if defined(LINUX) || defined(AIXPPC) || defined(J9ZOS390) || defined(OSX)
#define SOCKET_CAST(x) ((struct j9socket_struct*)  x)->sock
#else /* defined(LINUX) || defined(AIXPPC) || defined(J9ZOS390) || defined(OSX) */
/* all the non-unix ports use this variant */
/* The sockets returned as a j9socket_t (j9socket_struct*) are not actual structs, we just pretend that the pointer is a struct and never dereference it.
*/
#ifdef NO_LVALUE_CASTING
#define SOCKET_CAST(x) (*((OSSOCKET *) &(x)))
#else /* NO_LVALUE_CASTING */
#define SOCKET_CAST(x) ((OSSOCKET)x)
#endif /* !NO_LVALUE_CASTING */
#endif /* defined(LINUX) || defined(AIXPPC) || defined(J9ZOS390) || defined(OSX) */



md5-e0ea7c15407a116c8473bf6c3ea3384d



//The Plan for OMR
typedef struct linger omrlinger;



md5-04aed1addb090b515f906fa87be5b9dc



// j9sock
typedef struct linger OSLINGER;



md5-903b4e2ae7b7580f3170f285004eb2f0



// j9sock
typedef struct ip_mreq OSIPMREQ;
typedef struct ipv6_mreq OSIPMREQ6;



md5-de64d0bdf815f424e82e6fe77475bf14



// j9sock
typedef struct j9sockaddr_struct {
#if defined(IPv6_FUNCTION_SUPPORT) || (defined(WIN32))
    OSSOCKADDR_STORAGE addr;
#else
    OSSOCKADDR addr;
#endif
} j9sockaddr_struct;

/*
* Socket structure on windows requires 2 sockets due to the fact that microsoft does not
* handle ipv6-mapped ipv6 addresses.  Therefore we need to listen to sockets on both
* the ipv6 and ipv4 stacks, when in a mode where we support ipv6.
*/
typedef struct j9socket_struct {
#if defined(WIN32)
    OSSOCKET ipv4;
    OSSOCKET ipv6;
    uint8_t flags;
#else /* WIN32 */
    OSSOCKET sock;
    uint16_t family;
#endif /* !WIN32 */
} j9socket_struct;

typedef struct j9hostent_struct {
    OSHOSTENT *entity;
} j9hostent_struct;



md5-d84691c464257eb9cad6bea276c7ffc7



// j9sock
/* structure for returning either and IPV4 or IPV6 ip address */
typedef struct j9ipAddress_struct {
    union {
#if defined(IPv6_FUNCTION_SUPPORT) || ((defined(WIN32)))
        uint8_t                 bytes[sizeof(struct in6_addr)];
#else
        uint8_t                 bytes[sizeof(struct in_addr)];
#endif
        struct in_addr      inAddr;
#if defined(IPv6_FUNCTION_SUPPORT) || ((defined(WIN32)))
        struct in6_addr     in6Addr;
#endif
        } addr;
    uint32_t    length;
    uint32_t   scope;
}  j9ipAddress_struct;

Will add this in later if need to.

I attached files that I put together here:
1) List of Basic data structures and APIs that Unix and Windows has. For Windows, on their website, it is recommended that we use the UNICODE functions and data structures but they also provide a list of ANSI functions and data structures.
unix_and_windows_socket_api.docx

2) A list of all the j9sock.c library function
J9sock_common_functions.docx

Attn: @DanHeidinga
@caohaley Starting with data-structures is not a good idea. First, you need to describe/propose the new socket API. Before proposing the new socket API, you need to study the existing the j9sock API.

Next steps:

  1. Link the j9sock functions to Operating System (OS) specific system calls.

| j9sock function | description | Linux | AIX | zOS | OSX | Windows |
| ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- |
| j9sock_accept | LINK | accept | accept | accept | accept | accept |
| xxxx | xxxx | .... | .... | .... | .... | .... |

  1. Do the j9sock functions need to separately handle IPv4/IPv6 or TCP/UDP scenarios?

| j9sock function | IPv4/IPv6 | UDP/TCP |
| ------------- | ------------- | ------------- |
| j9sock_accept | Yes (Windows) | No |
| xxxx | xxxx | .... | .... |

  1. After you have studied the existing j9sock API, then you need to propose a minimalist socket API for OMR since many j9sock functions may not be required. At the start, the API design can have function names and their description. Moving forward, you will need to specify function prototypes. At this stage, you will propose data structures which need to be platform agnostic. You will also need to make sure that your design cleanly handles IPv4/IPv6 and UDP/TCP scenarios.

Side notes:

  • It was difficult to read code in the Word documents. Use Code and Syntax Highlighting in Github.
  • Tables should be used since they convey information neatly and in a compressed manner: Organizing information with tables in Github. I also recommend usage of hyperlinks for sharing web-links and permalinks for sharing code.
  • I’m not quite familiar with J9STPF socket API and will need help with that: J9ZTPF macro covers the implementation for the zTPF system which is a real time OS for mainframes. zTPF should be similar to other Unix systems (Linux, AIX, zOS, OSX). There may be subtle differences. We don't have zTPF machines for testing. We leave placeholders for the zTPF implementation, and the zTPF team ports the implementation if needed.
  • Other data-structure related questions can be discussed once the API has been proposed.
  1. Table for comparing j9sock functions and Operating System (OS) specific system calls.
  • Connection Related Calls

| j9sock function | description | LINUX | AIX | zOS | OSX | Windows |
|---|---|---|---|---|---|---|
| j9sock_accept | LINK | accept | accept | accept | accept | accept |
| j9sock_bind | LINK | bind | bind | bind | bind | bind |
| j9sock_close | LINK | close | close | close | close | close|
| j9sock_connect | LINK | connect | connect | connect | connect | connect |
| j9sock_freeaddrinfo | LINK | freeaddrinfo | freeaddrinfo | freeaddrinfo | freeaddrinfo | freeaddrinfo |
| j9sock_getaddrinfo | LINK | getaddrinfo | getaddrinfo | getaddrinfo | getaddrinfo | getaddrinfo |
| j9sock_listen | LINK | listen | listen | listen | listen | listen |
| j9sock_read | LINK | recv | recv | recv | recv | recv |
| j9sock_readfrom | LINK | recvfrom | recvfrom | recvfrom | recvfrom | recvfrom |
| j9sock_shutdown | LINK | shutdown | shutdown | shutdown | shutdown | shutdown |
| j9sock_socket | LINK | socket | socket | socket | socket | socket |
| j9sock_write | LINK | send | send | send | send | send |
| j9sock_writeto | LINK | sendto | sendto | sendto | sendto | sendto |

  • I/O Multiplexing

| j9sock function | description | LINUX | AIX | zOS | OSX | Windows |
|---|---|---|---|---|---|---|
| j9sock_select | LINK | select | select | select | select | select |
| j9sock_fdset_zero, j9sock_fdset_set, j9sock_fdset_clr, j9sock_fdset_isset | LINK | FD_ZERO, FD_SET, FD_CLR, FD_ISSET | FD_SET, FD_CLR, FD_ISSET | FD_ZERO, FD_SET, FD_CLR, FD_ISSET | FD_ZERO, FD_SET, FD_CLR, FD_ISSET | FD_ZERO, FD_SET, FD_CLR, FD_ISSET |

  • Accessing Information of Host or Connected Peer

| j9sock function | description | LINUX | AIX | zOS | OSX | Windows |
|---|---|---|---|---|---|---|
| j9sock_gethostbyaddr | LINK | gethostbyaddr Obslete, should use getaddrinfo() and getnameinfo() instead | gethostbyaddr | gethostbyaddr Moved to obsolescence in Single UNIX Specification, Version 3 and may be withdrawn in a future version. | gethostbyaddr | gethostbyaddr No longer recommended for use as of Windows Sockets 2 and the getnameinfo() should be used. However, gethostbyaddr is capable of returning a NetBIOS name whereas getnameinfo() is not |
| j9sock_gethostbyname | LINK | gethostbyname Obsolete, use getaddrinfo() and getnameinfo() instead | gethostbyname | gethostbyaddr Moved to obsolescence in Single UNIX Specification, Version 3 and may be withdrawn in a future version | gethostbyname | gethostbyname Deprecated by the introduction of the getaddrinfo function |
| j9sock_gethostname | LINK | gethostname | gethostname | gethostname | gethostname | gethostname |
| j9sock_getnameinfo | LINK | getnameinfo | getnameinfo | getnameinfo | getnameinfo | getnameinfo |
| j9sock_getpeername | LINK | getpeername | getpeername | getpeername | getpeername | getpeername |
| j9sock_getsockname | LINK | getsockname | getsockname | getsockname | getsockname | getsockname |

  • IP Address Conversions

| j9sock function | description | LINUX | AIX | zOS | OSX | Windows |
|---|---|---|---|---|---|---|
| j9sock_htonl | LINK | htonl | htonl | htonl | htonl | htonl |
| j9sock_htons | LINK | htons | htons | htons | htons | htons |
| j9sock_inetaddr | LINK | inet_addr | inet_addr | inet_addr | inet_addr | inet_addr |
| j9sock_ntohl | LINK | ntohl | ntohl | ntohl | ntohl | ntohl |
| j9sock_ntohs | LINK | ntohs | ntohs | ntohs | ntohs | ntohs |

  1. Do the j9sock functions need to separately handle IPv4/IPv6 or TCP/UDP scenarios?
  • Connection Related Calls

j9sock function | IPv4/IPv6 | UDP/TCP
-- | -- | --
j9sock_accept | Yes (Windows), will need to configure address family | No
j9sock_bind | No, as long as parameters are in the same address family | No
j9sock_close | No | No
j9sock_connect | Yes (Windows), will need to configure address family | No. But for UDP, connect may be called multiple times to change/set/unset association with a destination addres. For TCP, it may be only called once to make a connection.
j9sock_freeaddrinfo | No | No
j9sock_getaddrinfo | No | No
j9sock_listen | No | Yes, UDP does not use listen and thus not supported
j9sock_read | No | Yes basically. This function only applied to connected sockets (zOS). Recv is usually only used for connected sockets but could be used for connectionless sockets as well (Linux, AIX, OSX, Windows)
j9scok_readfrom | No | Yes basically. Can be used with unconnected/connectionless sockets or connected sockets as well (Linux, OSX, Windows). Used with unconnected(AIX)/datagram(zOS) sockets
j9sock_shutdown | No | Yes. Shutdown is for full duplex connections but if you want to shutdown a connected datagram socket, it is possible (Linux, Windows).
j9sock_socket | No. You simply pass in the argument whether or not you want a IPv4 socket or IPv6 socket | No. You simply pass in the argument whether or not you want a stream, datagram or other kind of socket
j9sock_write | No | Yes. Only used in connected mode
j9sock_writeto | No | Yes. Can be used with connected or unconnected sockets (Linux, zOS, OSX, Windows). Used with unconnected sockets (AIX)

  • I/O Multiplexing

j9sock function | IPv4/IPv6 | UDP/TCP
-- | -- | --
j9sock_select | No | Yes. Works for connected stream sockets.
j9sock_fdset_zero, j9sock_fdset_set, j9sock_fdset_clr, j9sock_fdset_isset | No | No

  • Accessing Information of Host or Connected Peer

j9sock function | IPv4/IPv6 | UDP/TCP
-- | -- | --
j9sock_gethostbyaddr | Yes. May not work well with IPv6 | No
j9sock_gethostbyname | Yes. Does not work with IPv6 | No
j9sock_gethostname | No | No
j9sock_getnameinfo | No | No
j9sock_getpeername | Yes. Will need to configure address storage structure passed in for IPv4 vs IPv6 | No
j9sock_getsockname | Yes. Will need to configure address storage structure passed in for IPv4 vs IPv6 | No

  • IP Address Conversions

j9sock function | IPv4/IPv6 | UDP/TCP
-- | -- | --
j9sock_htonl | Yes if converting address. | No
j9sock_htons | Yes if converting address. | No
j9sock_inetaddr | Yes. Does not work for IPv6 | No
j9sock_ntohl | Yes if converting address. | No
j9sock_ntohs | Yes if converting address. | No

Unfavourable circumstance: Some of j9sock implementations outdated or contains some stale code. We will redesign/clean up the j9sock code and make it part of OMR implementations.

The design of how to set up the data structures and the functions are all based off j9sock implementations. They're minimalised, by removing what I think are no longer needed, or fixed so that it can use the current network programming APIs.

This minimalistic socket API proposal will only contain the basic socket functions needed for connection. There are more functions needed to send/receive data, host information queries and so on. They will be included after the minimalistic socket API is implemented.

Functions included in the minimalistic socket API proposal:

  1. getaddrinfo
  2. freeaddrinfo
  3. socket
  4. bind
  5. listen
  6. connect
  7. accept
  8. close

OMR Socket Data Structures:

  • Definition of OMRSockAddrInfoNode and omrsock_addrinfo_t
/* addrinfo struct
 * - data structure for getaddrinfo and freeaddrinfo functions
 * - OMRAddrInfo* addrInfo will be different depending on the Operating System's respective addrinfo 
 *   structures (defined later on) and is the pointer to the first addrinfo of the linked list
 * - int length counts how many addrinfo nodes there are in the linked list
 */

typedef struct OMRSockAddrInfoNode {
    OMRAddrInfo* addrInfo;
    int length;
} OMRSockAddrInfo;
typedef OMRSockAddrInfoNode* omrsock_addrinfo_t;
  • Definition of omrsock_socket_t
/* socket pointer
 * - pointer to a socket descriptor
 * - points to a OMRSocket which is an SOCKET type for 
 *   Windows but int for all other operating systems
 * - this will be used by the potential user to pass into function calls as a parameter
 */
typedef OMRSocket* omrsock_socket_t;
  • Definition of OMRSockSockAddr and omrsock_sockaddr_t
/* sockaddr struct
 * - used to store ip addresses. If IPv6 is supported, then we use omrsockaddr_storage,
 *   which can store IPv4 or Ipv6 addresses.
 */
typedef struct OMRSockSockAddr {
#if defined(IPv6_FUNCTION_SUPPORT)
    OMRSockAddrStorage addr;
#else
    OMRSockAddr addr;
#endif
} OMRSockSockAddr;
typedef OMRSockSockAddr* omrsock_sockaddr_t;
  • Definition of OMRHostent
/* hostent may be still used by some programmers but isn't used as much anymore
 */
typedef struct hostent OMRHostent;

Data types for Internal Use:

  • OMRSocket, the socket descriptor
  • OMRSockAddr, the generic sockaddr parameter for passing into many API functions, may be casted both ways from and to sockaddr_in and sockaddr_in6
  • OMRSockAddrIn, the IP address for IPv4 sockets
  • OMRSockAddrIn6, the IP address for IPv6 sockets
  • OMRSockAddrStorage, the storage is large enough for either IPv4 or IPv6 address, used for when you don't know which address family the address will be
  • OMRAddrInfo, the addrinfo structure, which is a linked list of addresses and related information (such as address family, protocols, etc.)
// OMR unix_include
typedef int OMRSocket;
typedef struct sockaddr OMRSockAddr;
typedef struct sockaddr_in OMRSockAddrIn; /* for IPv4 addresses*/
typedef struct sockaddr_in6 OMRSockAddrIn6; /* for IPv6 addresses*/
typedef struct sockaddr_storage OMRSockAddrStorage; /* Big enough storage for IPv4 or IPv6 addresses */
typedef struct addrinfo OMRAddrInfo; /* addrinfo structure for both IPv4 and IPv6 */

// OMR win32_include
typedef SOCKET OMRSocket;
typedef SOCKADDR OMRSockAddr; /* for IPv4 */
typedef SOCKADDR_IN OMRSockAddrIn;  /* for IPv4 addresses*/
typedef SOCKADDR_IN6 OMRSockAddrIn6;  /* for IPv6 addresses*/
typedef struct sockaddr_storage OMRSockAddrStorage; /* Big enough storage for IPv4 or IPv6 addresses */
typedef struct addrinfoW OMRAddrInfo;  /* addrinfo structure – Unicode for both IPv4 and IPv6 */

OMRSock Functions:

omrsock_getaddrinfo:

/**
 * Answers a list of addresses as an opaque struct in "result".
 * 
 * Use the following functions to extract the details:
 * \arg \ref omrsock_getaddrinfo_length
 * \arg \ref omrsock_getaddrinfo_name
 * \arg \ref omrsock_getaddrinfo_address
 * \arg \ref omrsock_getaddrinfo_family
 *
 * @param[in] portLibrary The port library.
 * @param[in] name The name of the host in either host name format or in IPv4 or IPv6 accepted notations.
 * @param[in] hints Hints on what results are returned and how the response if formed (can be NULL for default action). Use omrsock_getaddrinfo_create_hints to create the hints
 * @param[out] result An opaque pointer to a list of results (omraddrinfo_struct must be preallocated).
 *
 * @return  0, if no errors occurred, otherwise the (negative) error code.
 *
 * @note you must free the "result" structure with @ref omrsock_freeaddrinfo to free up memory.
 */
int32_t
omrsock_getaddrinfo(struct OMRPortLibrary *portLibrary, char *node, char *service, struct omrsock_addrinfo_t hints, struct omrsock_addrinfo_t result);

Notes for Implementation:

  • The potential users will be making a OMRSockAddrInfoNode to hold the outputs of omrsock_getaddrinfo.
  • To ensure that the potential users use the OMRSock without having to worry about different Operating Systems, they will be extracting data out of OMRSockAddrInfoNode using functions like omrsock_getaddrinfo_length, omrsock_getaddrinfo_name and on so. Therefore, they would use the index of the linked list to check information in the respective node. If they want to traverse the linked list, they would use a for-loop and extracting information individually using the index.
  • The potential users will use omrsock_getaddrinfo_create_hints to create the hints to pass into this function.

omrsock_freeaddrinfo:

/**
 * Frees the memory created by the call to @ref omrsock_getaddrinfo().
 *
 * @param[in] portLibrary The port library.
 * @param[in] handle Hints on what results are returned for getaddrinfo.
 *
 * @return  0, if no errors occurred, otherwise the (negative) error code.
 *
 */
int32_t
omrsock_freeaddrinfo(struct OMRPortLibrary *portLibrary, omrsock_addrinfo_t handle);

omrsock_socket:

/**
 * Creates a new socket descriptor and any related resources.
 *
 * @param[in] portLibrary The port library.
 * @param[out] sock Pointer to the omrsocket, to be allocated
 * @param[in] family The address family
 * \arg OMRSOCK_AFINET, for a IPv4 socket
 * \arg OMRSOCK_AFINET6, for a IPv6 socket
 * @param[in] socktype Specifies what type of socket is created
 * \arg OMRSOCK_STREAM, for a stream socket
 * \arg OMRSOCK_DGRAM, for a datagram socket
 * @param[in] protocol Type/family specific creation parameter
 *
 * @return  0, if no errors occurred, otherwise the (negative) error code.
 */
int32_t
omrsock_socket(struct OMRPortLibrary *portLibrary, omrsock_socket_t sock, int32_t family, int32_t socktype,  int32_t protocol);
  1. Check if family, socktype, protocol exists. If it doesn't, set return values accordingly.
  2. Call the Socket creating function using API for that operating system.
  3. Check if API function call was successful.

Implementation Note:

  • It seems like j9sock_socket() creates both ipv4 and ipv6 sockets for Windows Implementation if user specifies J9ADDR_FAMILY_UNSPEC. However it doesn't support that on Unix. I think for now, the user themselves can create 2 sockets if they want to. I don't think we need to implement that functionality in our omrsock_socket code yet.

omrsock_bind:

/**
 * The bind function is used on an unconnected socket before subsequent 
 * calls to the connect or listen functions. When a socket is created with a 
 * call to the socket function, it exists in a name space (address family), but 
 * it has no name assigned to it. Use bind to establish the local association 
 * of the socket by assigning a local name to an unnamed socket.
 *
 * @param[in] portLibrary The port library.
 * @param[in] sock The socket that will be be associated with the specified name.
 * @param[in] addr Address to bind to socket.
 *
 * @return  0, if no errors occurred, otherwise the (negative) error code.
 */
int32_t
omrsock_bind(struct OMRPortLibrary *portLibrary, omrsock_socket_t sock, omrsock_sockaddr_t addr);
  1. Get the length of the address by using sizeof(), ipv4 or ipv6 length
  2. Call the bind function using API for that operating system.
  3. Check if API function call was successful.
  4. Can double check with getnameinfo to see if bind was successful (j9sock unix implementation)

omrsock_listen:

/**
 * Set the socket to listen for incoming connection requests.  This call is made prior to accepting requests,
 * via the @ref omrsock_accept function.  The backlog specifies the maximum length of the queue of pending connections,
 * after which further requests are rejected.
 *
 * @param[in] portLibrary The port library.
 * @param[in] sock Pointer to the socket to modify.
 * @param[in] backlog The maximum number of queued requests.
 *
 * @return  0, if no errors occurred, otherwise the (negative) error code.
 */
int32_t
omrsock_listen(struct OMRPortLibrary *portLibrary, omrsock_socket_t sock, int32_t backlog);
  1. Check if socket is a datagram socket. Listen does not work for datagram sockets.
  2. Call the listen function using API for that operating system.
  3. Check if API function call was successful.

omrsock_connect:

/**
 * Establish a connection to a peer.
 *
 * For stream sockets, it first binds the socket if it hasn't already been done. Then, it
 * tries to set up a connection.
 * 
 * For datagram sockets, connect function will set up the peer information. No actual
 * connection is made. Subsequent calls to connect can be made to change destination address.
 *
 * It is not recommended that users call this
 * function multiple times to determine when the connection attempt has succeeded.
 * Instead, the omrsock_select() function can be used to determine when the socket 
 * is ready for reading or writing.
 * 
 * @param[in] portLibrary The port library.
 * @param[in] sock pointer to the unconnected local socket.
 * @param[in] addr  pointer to the sockaddr, specifying remote host/port.
 *
 * @return  0, if no errors occurred, otherwise the (negative) error code.
 */
omrsock_connect(struct OMRPortLibrary *portLibrary, omrsock_socket_t sock, omrsock_sockaddr_t addr)
  1. Get the length of the address by using sizeof(), ipv4 or ipv6 length.
  2. Identify if it is a request to disconnect.
  3. Identify if it is a steam or datagram.
  4. Call the connect function using API for that operating system.
  5. Check if API function call was successful.

omrsock_accept:

/**
 * The accept function extracts the first connection on the queue of pending connections 
 * on socket serverSock. It then creates a new socket and returns a handle to the new socket. 
 * The newly created socket is the socket that will handle the actual the connection and 
 * has the same properties as socket serverSock.  
 *
 * The accept function can block the caller until a connection is present if no pending 
 * connections are present on the queue.
 *
 * @param[in] portLibrary The port library.
 * @param[in] serverSock  A omrsock_socket_t that tries to accept a connection.
 * @param[in] addrHandle An optional pointer to a buffer that receives the address of the connecting 
 * entity, as known to the communications layer. The exact format of the addr parameter 
 * is determined by the address family established when the socket was created
 * @param[out] sockHandle A pointer to a omrsock_socket_t which will point to the newly created 
 * socket once accept returns successfully
 *
* @return   0, if no errors occurred, otherwise the (negative) error code.
 */

int32_t
omrsock_accept(struct OMRPortLibrary *portLibrary, omrsock_socket_t serverSock, omrsock_sockaddr addrHandle, omrsock_socket_t *sockHandle);
  1. Get the length of the address by using sizeof(), ipv4 or ipv6 length
  2. Call the accept function using API for that operating system.
  3. Check if API function call was successful. If successful, we need to copy new socket information into sockHandle.

omrsock_close:

/**
 * The close function closes a socket. Use it to release the socket descriptor socket so 
 * further references to socket will fail.
 *
 * @param[in] portLibrary The port library.
 * @param[in] sock j9socket_t  which will be closed.
 *
 * @return  0, if no errors occurred, otherwise the (negative) error code.
 */
int32_t
omrsock_close(struct OMRPortLibrary *portLibrary, omrsock_socket_t sock)
  1. Free allocated memory if there are any.
  2. Call the shutdown function using API for that operating system and check if successful. (optional)
  3. Call the close function using API for that operating system (closesocket if windows) and check if successful.

Questions:

  1. There are both ANSI and Unicode implementations for functions that gets or sets host information for Windows Socket API (for example getaddrinfo vs GetAddrInfoW). It is recommended on the Microsoft website to use Unicode implementations. Should we support both ANSI and Unicode implementations or just Unicode? In j9sock, it seems like it tries Unicode first and if that doesn't work, it then tries ANSI version.
  2. In j9sock, all j9sock functions(socket, connect, listen) takes care of the unspecified address family for Windows. Was there a reason why the j9sockaddr_struct had both ipv4 and ipv6 sockets in it? Is it still necessary now?
  3. Sometimes, it'll be easier for potential users if memory allocation is taken care of for the users. For example, getaddrinfo requires a hints structure but if hints structure is allocated and deallocated for the users, it'll be more convenient for them and there won't be memory leaks. j9sock does this by using J9SocketPTB which is a Per-thread buffer for platform-dependent socket information. Should we do something like that too?

Additional Notes

Establish Connections and Communication:

The way to establish communications doesn't change with our OMRSock API. The potential user will simply have to call the OMR version of these function calls. For example, omrsock_socket() instead of socket(), omrsock_bind() instead of bind() and so on.

Typical Order of Function Calls to Establish TCP communication:
_Server:_
socket() -> bind() -> listen() -> accept() -> read()/write() -> close()

After server calls listen(), it is ready to accept connection requests. The connection requests are placed in a queue of the size that you set when you called listen(). When server accepts a connection, it accepts the first connection request in the queue and a new socket is created. While it is setting up the connection in accept(), it blocks communication. Once connection is set up, the communication happens through read() or write().

_Client:_
socket() -> connect() -> read()/write() -> close()

The client calls connect() to attempt setting up a connection with the server. connect() returns after it successfully connects (or can't connect returning an error code) to the server.

Typical Order of Function Calls to Establish UDP communication:
_Server:_
socket() -> bind() -> sendto()/recvfrom() -> close()

The server needs to bind() to the interfaces that it wants to receive from. The sendto(), recvfrom() contains the address of the peer that receives or sent out the data.

_Client:_
socket() -> sendto()/recvfrom() -> close()

The client uses sendto() and recvfrom() to send to a specified address.


Use of getaddrinfo

However, omrsock_getaddrinfo() is different from getaddrinfo() for the users:

The potential users will need to create hints using omrsock_getaddrinfo_create_hints() and pass the hints into the omrsock_getaddrinfo(). The hints will specify what kind of address you are looking for, what address family, protocol, etc. and the results will be filtered accordingly. omrsock_getaddrinfo() essentially takes the hostname and service name and fills in the information of the host including address, address family, protocols etc. for you. It returns a linked list of OMRAddrInfo inside, with the pointer to the first OMRAddrInfo in OMRSockAddrInfoNode.

omrsock_getaddrinfo_create_hints() -> omrsock_getaddrinfo()

Usually, programmers traverse the linked list and bind to the first one they can.

An example using socket API on an Operating System like Linux directly:

#define PORT "3333"

struct addrinfo hints;
struct addrinfo* res;
struct addrinfo* p;
int sock;

/* Set hints to only return results that are IPv6 and Stream */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;

getaddrinfo(NULL, PORT, &hints, &res);

/* Traverse through results */
for (p = res; p != NULL; p = p->ai_next){
    /* Try to create a socket with addrinfo and bind to it */
    sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if(-1 == sock){
        /* making socket failed */
        continue;
    }
    if(-1 == bind(sock, p->ai_addr, p->ai_addrlen)){
        /* binding socket failed */
        continue;
    }
    break;
}
/* Now you can try to connect and then send/receive data as you wish */

Using our OMRSock API:

#define PORT "3333"

OMRPORT_ACCESS_FROM_OMRPORT(portLibrary);

struct OMRSockAddrInfoNode hintsStruct;
struct OMRSockAddrInfoNode resStruct;
omrsock_addrinfo_t hints = &hintsStruct;
omrsock_addrinfo_t res = &resStruct;
OMRSOCKET sock;

/* Set hints to only return results that are IPv6 and Stream */
omrsock_getaddrinfo_create_hints(hints, OMR_AFINET6, OMR_STREAM, 0, 0);

omrsock_getaddrinfo("NULL", PORT, hints, res);

/* Traverse through results */
for (int i = 0; i < res->length; i++){
    int family = 0;
    int socktype = 0;
    int protocol = 0;

    omrsock_getaddrinfo_family(res, &family, i);
    omrsock_getaddrinfo_socktype(res, &socktype, i);
    omrsock_getaddrinfo_protocol(res, &protocol, i);

    /* Try to create a socket and bind to it */
    if(0 != omrsock_socket(&sock, family, socktype, protocol)){
        /* making socket failed */
        continue;
    }
    if(0 != omrsock_bind(&sock, p->ai_addr, p->ai_addrlen)){
        /* binding socket failed */
        continue;
    }
    break;
}

/* Now you can try to connect and then send/receive data as you wish */

As can be seen, our OMRSock API will need the users to traverse using an index, rather than traversing through an linked list directly.

@caohaley Good work. It will be easier to review if the above code is in a pull request with stubs for API functions. Here is some feedback:

1) You will either be using OMRSockAddr or OMRSockAddrStorage depending on IPv6_FUNCTION_SUPPORT.

typedef struct OMRSockSockAddr {
#if defined(IPv6_FUNCTION_SUPPORT)
    OMRSockAddrStorage addr;
#else
    OMRSockAddr addr;
#endif
} OMRSockSockAddr;

You don't need OMRSockSockAddr. Instead, you can use:

#if defined(IPv6_FUNCTION_SUPPORT)
typedef struct sockaddr_storage OMRSockSocketAddr; /* Big enough storage for IPv4 or IPv6 addresses */
#else /* defined(IPv6_FUNCTION_SUPPORT) */
typedef struct sockaddr OMRSockSocketAddr;
#endif /* defined(IPv6_FUNCTION_SUPPORT) */

typedef OMRSockSocketAddr* omrsock_socketaddr_t;
  1. IPv6_FUNCTION_SUPPORT: Make a note on how/where/who defines this flag. It seems unclear at this point.

  2. When drafting the PR, follow ... Comment conditional compilation directives (#if, #ifdef, #ifndef, #else, #elif, #endif).

  3. Consistency in naming. Should all struct names start with OMRSock*? OMRAddrInfo (doesn't have OMRSock)? OMRSockSockAddr (SockSock gets confusing).

  4. omrsock_getaddrinfo: input variables char *node, char *service are undefined in the description.

  5. OMR uses Doxygen comments. So, \arg and \ref [incorrect syntax] should be replaced with @arg and @ref.

  6. Remaining functions look good: freeaddrinfo, socket, bind, listen, connect, accept and close.

  7. How are API users going to read, write, sendto and recvfrom? Do these operations need API functions?

  8. Using keyword struct: struct OMRPortLibrary *portLibrary and omrsock_addrinfo_t hints. struct does not need to be added to var_t type-structures. It is only used with Var type-structures. This pattern is noticeable within OMR. In getaddrinfo, you use struct with omrsock_addrinfo_t hints/result, which is not needed.

Was there a reason why the j9sockaddr_struct had both ipv4 and ipv6 sockets in it? Is it still necessary now?

Only one field OSSOCKADDR_STORAGE or OSSOCKADDR is used at a time in j9sockaddr_struct depending upon IPv6_FUNCTION_SUPPORT. You don't need both if only one is used. Refer to point 1.

getaddrinfo requires a hints structure but if hints structure is allocated and deallocated for the users, it'll be more convenient for them and there won't be memory leaks. j9sock does this by using J9SocketPTB which is a Per-thread buffer for platform-dependent socket information. Should we do something like that too?

API should abstract such functionality. So, propose API functions to manage the hints structure.

Should we support both ANSI and Unicode implementations or just Unicode?

We should only support Unicode (recommended) functions if they are available on all Windows variants supported by OMR.

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment on the issue or it will be closed in 60 days.

PR connected to this issue are still being created/merged so this issue is not stale.

Linux to omrsock API Mapping

|Linux Function Name| omrsock API Function Name|
|---|---|
| - | omrsock_getaddrinfo_create_hints|
|getaddrinfo | omrsock_getaddrinfo|
| - | omrsock_addrinfo_length|
| - | omrsock_addrinfo_family|
| - | omrsock_addrinfo_socktype|
| - | omrsock_addrinfo_protocol|
|freeaddrinfo | omrsock_freeaddrinfo|
| - | omrsock_sockaddr_init|
| - | omrsock_sockaddr_init6|
|socket | omrsock_socket|
| - | omrsock_socket_getfd|
|bind | omrsock_bind|
|listen | omrsock_listen|
|accept | omrsock_accept|
|send | omrsock_send|
|sendto | omrsock_sendto|
|recv | omrsock_recv|
|recvfrom | omrsock_recvfrom|
| - | omrsock_pollfd_init|
| - | omrsock_get_pollfd_info|
|poll | omrsock_poll|
|FD_ZERO | omrsock_fdset_zero|
|FD_SET | omrsock_fdset_set|
|FD_CLR | omrsock_fdset_clr|
|FD_ISSET | sock_fdset_isset|
|select | omrsock_select|
|close | omrsock_close|
|htons | omrsock_htons|
|htonl | sock_htonl|
|inet_pton | omrsock_inet_pton|
|fcntl | omrsock_fcntl|
| - | omrsock_timeval_init|
| - | omrsock_linger_init|
|setsockopt | sock_setsockopt_int|
|setsockopt | sock_setsockopt_linger|
|setsockopt | sock_setsockopt_timeval|
|getsockopt | sock_getsockopt_int|
|getsockopt | sock_getsockopt_linger|
|getsockopt | sock_getsockopt_timeval|

Linux to omsock API Implementations Mapping

Linux Implementations | omrsock API Implementations | omrsock API Memory Management ---|---|--- || struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; | omrsock_addrinfo_t hintsPtr = NULL;
omrsock_getaddrinfo_create_hints(OMRPORTLIB, &hintsPtr, OMRSOCK_AF_INET, OMRSOCK_STREAM, OMRSOCK_IPPROTO_DEFAULT, 0); | omrsock API allocates the hints structure for user using per thread buffer defined in omr port library. User does not need to free this memory. struct addrinfo *resultsPtr;
getaddrinfo("localhost", "4930", &hints, &resultsPtr); | omrsock_addrinfo_t resultPtr = NULL;
omrsock_getaddrinfo(OMRPORTLIB, "localhost", "4930", hintsPtr, resultPtr); | omrsock API allocates addrinfo structure for user. Free with @ref omrsock_freeaddrinfo. User needs to manually count. | uint32_t length = 0;
OMRPORTLIB->sock_addrinfo_length(OMRPORTLIB, &resultsPtr, &length); | User needs to allocate length. int32_t family = resultsPtr->ai_family; | int32_t family = 0;
omrsock_addrinfo_family(OMRPORTLIB, &resultsPtr, 0, &family); | User needs to allocate family. int32_t socktype = resultsPtr->ai_socktype; | int32_t socktype = 0;
omrsock_addrinfo_socktype(OMRPORTLIB, &resultsPtr, 0, &socktype); | User needs to allocate socktype. int32_t protocol = resultsPtr->ai_protocol; | int32_t protocol = 0;
sock_addrinfo_protocol(OMRPORTLIB, &resultsPtr, 0, &protocol); | User needs to allocate protocol. freeaddrinfo(resultsPtrs); | omrsock_freeaddrinfo(OMRPORTLIB, &result); | Frees memory allocated in @ref omrsock_getaddrinfo. struct sockaddr_in sockAddr;
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr = networkOrderAddr;
sockAddr.sin_port = networkOrderPort;| OMRSockAddrStorage sockAddr;
omrsock_sockaddr_init(OMRPORTLIB, &sockAddr, OMRSOCK_AF_INET, networkOrderAddr, networkOrderPort); | User allocates socket address structure. For network order, refer to @ref omrsock_inet_pton, @ref omrsock_htons, @ref omrsock_htonl. struct sockaddr_in6 sockAddr;
sockAddr.sin_family = AF_INET6;
sockAddr.sin_addr = networkOrderAddr6;
sockAddr.sin_port = networkOrderPort;| OMRSockAddrStorage sockAddr;
omrsock_sockaddr_init6(OMRPORTLIB, &sockAddr, OMRSOCK_AF_INET, networkOrderAddr6, networkOrderPort); | User allocates socket address structure. For network order, refer to @ref omrsock_inet_pton, @ref omrsock_htons, @ref omrsock_htonl. No need for this function. | int32_t fd = omrsock_socket_getfd(OMRPORTLIB, socket); | - fd = socket(AF_INET, SOCK_STREAM, 0); | omrsock_socket_t socket;
omrsock_socket(OMRPORTLIB, OMRSOCK_AF_INET, OMRSOCK_STREAM, OMRSOCK_IPPROTO_DEFAULT); | omrsock API allocates the socket structure for user. Free with @ref omrsock_close to close socket. struct sockaddr_in addr;
bind(socketFd, (struct sockaddr*)&addr, sizeof addr); | OMRSockAddrStorage addr;
omrsock_bind(OMRPORTLIB, socket, &addr); | User allocates socket address structure. listen(sockFd, 10); | omrsock_listen(OMRPORTLIB, serverSocket, 10); | - struct sockaddr_storage newAddr;
socklen_t newAddrSize = sizeof newAddr;
int32_t newFd = accept(serverFd, (struct sockaddr *)&newAddr, &newAddrSize); | OMRSockAddrStorage newAddr;
omrsock_socket_t newSocket = NULL;
omrsock_accept(OMRPORTLIB, serverSocket, &addr, &socket); | User allocates both socket address structure and socket structure for newly returned connected server socket. int32_t bytesSent = send(socketFd, msg, bytesToSend, 0); | int32_t bytesSent = omrsock_send(OMRPORTLIB, socket, (uint8_t *)msg, bytesToSend, 0); | - char buf[SIZE];
int32_t bytesRecv = recv(socketFd, buf, bytesToRecv, 0); | char buf[SIZE];
int32_t bytesRecv = omrsock_recv(OMRPORTLIB, socket, (uint8_t *)buf, bytesToRecv, 0); | - int32_t bytesSent = sendto(socketFd, msg, bytesToSend, 0, recvAddr, recvAddrLen); | int32_t bytesSent = omrsock_sendto(OMRPORTLIB, sendSocket, (uint8_t *)msg, bytesToSend, 0, &recvAddr); | - char buf[SIZE];
int32_t bytesRecv = recvfrom(recvSocket, buf, bytesToRecv, 0, &sentFromAddr, &sentFromAddrLen); | uint8_t buf[SIZE];
int32_t bytesRecv = sock_recvfrom(OMRPORTLIB, recvSocket, (uint8_t *)buf, bytesToRecv, 0, &sentFromAddr); | - struct pollfd pollArray[N];
pollArray[0].fd = 0;
pollArray[0].events = POLLIN;| OMRPollFd pollArray[N];
omrsock_pollfd_init(OMRPORTLIB, &pollArray[0], socket, OMRSOCK_POLLIN); | User allocates poll fd structure. int32_t sockFd = pollArray[0].fd;
int16_t revents = pollArray[0].revents; | omrsock_socket_t socketPtr = NULL;
int16_t revents = 0;
omrsock_get_pollfd_info(OMRPORTLIB, &pollArray[0], &socketPtr, &revents); | User allocates socketPtr and revents to be returned. int32_t numEvents = poll(pfds, N, time); | int32_t numReady = omrsock_poll(OMRPORTLIB, pollArray, N, time); | - fd_set fdset;
FD_ZERO(&fdset); | OMRFdSet fdSet;
omrsock_fdset_zero(OMRPORTLIB, &fdSet); | User allocates fdset structure. fd_set fdset;
FD_SET(socketFd, &fdset); | OMRFdSet fdSet;
omrsock_fdset_set(OMRPORTLIB, socket, &fdSet); | User allocates fdset structure. fd_set fdset;
FD_CLR(socketFd, &fdset); | OMRFdSet fdSet;
omrsock_fdset_clr(OMRPORTLIB, socket, &fdSet); | User allocates fdset structure. fd_set fdset;
bool socketSet = FD_ISSET(socketFd, &fdset); | OMRFdSet fdSet;
bool socketSet = omrsock_fdset_isset(OMRPORTLIB, socket, &fdSet); | User allocates fdset structure. select(nfds, &readfds, NULL, NULL, &timeOut); | omrsock_select(OMRPORTLIB, &readfds, NULL, NULL, &timeOut); | - close(socketFd); | omrsock_close(OMRPORTLIB, socket); | Frees memory allocated in @ref omrsock_socket. uint16_t networkOrder = htons(val); | uint16_t networkOrder = omrsock_htons(OMRPORTLIB, val); | - uint32_t networkOrder = htonl(val); | uint32_t networkOrder = omrsock_htonl(OMRPORTLIB, val); | - int32_t addr;
inet_pton(AF_INET, "127.0.0.1", &addr); | int32_t addr;
omrsock_inet_pton(OMRPORTLIB, OMRSOCK_AF_INET, "127.0.0.1", &addr); | - fcntl(socketFd, F_SETFL, O_NONBLOCK); | omrsock_fcntl(OMRPORTLIB, socket, OMRSOCK_O_NONBLOCK); | - struct timeval time;
time.tv_sec = 2;
time.tv_usec = 500000;| OMRTimeval time;
omrsock_timeval_init(OMRPORTLIB, &time, 2, 500000); | User allocates timeval structure. struct linger linger;
linger.l_onoff = 1;
linger.l_linger = 2;| OMRLinger linger;
omrsock_linger_init(OMRPORTLIB, &linger, 1, 2); | User allocates linger structure. int32_t flag = 1;
setsockopt(socketFd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof flag);| int32_t flag = 1;
omrsock_setsockopt_int(OMRPORTLIB, socket, OMRSOCK_SOL_SOCKET, OMRSOCK_SO_KEEPALIVE, &flag); | - struct linger linger;
linger.l_onoff = 1;
linger.l_linger = 2;
setsockopt(socketFd, SOL_SOCKET, SO_LINGER, &linger, sizeof linger); | OMRLinger linger;
omrsock_linger_init(OMRPORTLIB, &linger, 1, 2);
omrsock_setsockopt_linger(OMRPORTLIB, socket, OMRSOCK_SOL_SOCKET, OMRSOCK_SO_LINGER, &linger); | - struct timeval time;
time.tv_sec = 2;
time.tv_usec = 500000;
setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, &time, sizeof time);| OMRTimeval time;
omrsock_timeval_init(OMRPORTLIB, &time, 2, 500000);
omrsock_setsockopt_timeval(OMRPORTLIB, socket, OMRSOCK_SOL_SOCKET, OMRSOCK_SO_RCVTIMEO, &timeVal); | - int32_t flag, flagLen;
getsockopt(socketFd, SOL_SOCKET, SO_KEEPALIVE, &flag, &flagLen); | int32_t flag;
omrsock_getsockopt_int(OMRPORTLIB, socket, OMRSOCK_SOL_SOCKET, OMRSOCK_SO_KEEPALIVE, &flag); | - struct linger linger;
int32_t len;
getsockopt(socketFd, SOL_SOCKET, SO_LINGER, &linger, &len); | OMRLinger linger;
omrsock_getsockopt_linger(OMRPORTLIB, socket, OMRSOCK_SOL_SOCKET, OMRSOCK_SO_LINGER, &linger); | - struct timeval time;
int32_t len;
getsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, &time, &len);| OMRTimeval time;
omrsock_getsockopt_timeval(OMRPORTLIB, socket, OMRSOCK_SOL_SOCKET, OMRSOCK_SO_RCVTIMEO, &time); | -

@mpirvu: Should this issue remain open if the work is complete and merged?

This issue can be closed as the work is complete.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

0xdaryl picture 0xdaryl  Â·  3Comments

rwy0717 picture rwy0717  Â·  6Comments

sajidahmed21 picture sajidahmed21  Â·  3Comments

aviansie-ben picture aviansie-ben  Â·  6Comments

mgaudet picture mgaudet  Â·  6Comments