On 2001-02-26 08:21:23, Stig Venaas <[EMAIL PROTECTED]> wrote:
> Sounds good to me. It's also impossible in a portable way to know if a
> socket is AF_INET, AF_INET6 or AF_LOCAL, so for socket() function etc.
> I've been thinking of doing something like this to store what domain
> a socket is in (maybe type and protocol too). So I think that all

We could add these fields to the php_sockbuf, along with the things listed below; in 
this implementation, a socketd will always have a php_sockbuf present.

I've been delving into it, and these are my thoughts:

When opening a socket there are 3 stages (and thus operations on the socket):

1. Create a socketd of appropriate domain/type/family (socket(2))
2. resolve the address to connect to (domain specific)
3. make the connection (connect_nonb)

If there is an error in one of these stages the reason might also need to be returned 
in a human readable form.

Once the socket is opened we have 3 remaining operations:

1. read
2. write
3. close

If we bring this together (and borrow from fopen_wrappers) we can have an extensible 
socket abstraction, where the protocol part of the hostname from the fsockopen call 
determines which underlying socket family to use.
The protocol name would map to a php_socket_ops structure, which might look something 
like this:

struct php_socket_ops {
    int (*socket)(void ** cookie, socklen_t * addrlen);
    int (*resolve)(void * cookie, struct sockaddr * addr, char * hostname, unsigned 
short port);
    int (*connect)(void * cookie, struct sockaddr * addr, struct timeval * timeout);
    int (*strerror)(void * cookie, int errno, char * buf, size_t buflen);
    int (*close)(void * cookie);
    size_t (*read)(void * cookie, char * buf, size_t buflen);
    size_t (*write)(void * cookie, char * buf, size_t buflen);
};

php_sockbuf {
    ...
    struct php_socket_ops * ops;
    void * cookie;
};

It should be fairly self explanatory how things work, but the socket, resolve and 
connect methods need a little more detail:

Since the size of a struct sockaddr varies depending on the domain of the socket, the 
call to socket returns the size required for this particular socket type.  It is the 
callers responsibility to allocate a buffer of that size and pass it to the resolve 
and connect methods.  After that point the buffer can be freed.

So, the pseudo code for php_fsockopen would like something like this:
(most error checking removed for brevity)

... parse/convert php args : hostname, port, timeout ...
... extract protocol string from hostname, default to "tcp://" ...

struct php_socket_ops * ops = fsockprotocol_to_ops(protocol);
void * cookie = NULL;
socklen_t socklen;
struct sockaddr * addr;
// create a socket and return the address length and cookie
int socketd = ops->socket(&cookie, &socklen);
// allocate address buffer
addr = emalloc(socklen);
// resolve the address
ops->resolve(cookie, addr, hostname, port);
// make the connection - note that the error code is returned
int errcode = ops->connect(cookie, addr, &timeout);
// free the addr buffer here to avoid complications in exiting this function
efree(addr);
// check success of connection
if (errcode != 0)
{
   // failed, so complain - get the error message
   char errbuf[256];
   ops->strerror(cookie, errcode, errbuf, sizeof(errbuf));
   // copy/use/print errbuf
   // cleanup
   ops->close(cookie);
   RETURN_FALSE;
}
// succeeded; record the socket and cookie in a php_sockbuf
struct php_sockbuf * sock = SOCK_FIND(socketd);
sock->ops = ops;
sock->cookie = cookie;
RETURN_TRUE;

Thats the basics.  The other side of the implementation, for tcp sockets might look 
something like this: (udp would be similar)

int tcp_socket(void ** cookie, socklen_t * len)
{
   int socketd = socket(AF_INET, SOCK_STREAM, 0);
   *len = sizeof(struct sockaddr_in);
   *cookie = (void*)socketd;
   return socketd;
}

int tcp_resolve(void * cookie, struct sockaddr * addr, char * hostname, unsigned short 
port)
{
   struct sockaddr_in * server = (struct sockaddr_in*)addr;
   server.sin_family = AF_INET;
   if (php_lookup_hostname(hostname, &server->sin_addr))
       return errno;
   server.sin_port = htons(port);
   return 0;
}

int tcp_connect(void * cookie, struct sockaddr * addr, struct timeval * timeout)
{
   if (connect_nonb((int)cookie, addr, sizeof(struct sockaddr_in), timeout) == 
SOCK_CONN_ERR)
       return errno;
   return 0;
}

// declared this way because the openSSL error functions are like this
int tcp_strerror(void * cookie, int errno, char * buf, size_t buflen)
{
   char * errstr = strerror(errno);
   size_t len = strlen(errstr);
   if (len > buflen)
      len = buflen;
   strncpy(buf, errstr, len);
   return 0;
}

int tcp_close(void * cookie)
{
   shutdown((int)cookie, 0);
   closesocket((int)cookie);
}

size_t tcp_read(void * cookie, char * buf, size_t buflen)
{
   return read((int)cookie, buf, buflen);
// or recv((int)cookie, buf, buflen, 0)
}

size_t tcp_write(void * cookie, char * buf, size_t buflen)
{
   return write((int)cookie, buf, buflen);
}

Hopefully, you all get the idea.

Possible problems that I can see with this are that some of the traditional socket 
operations might not be applicable to all the different types of sockets - if it does 
pose a problem, a possible solution might be to add another method to the ops 
structure.

Another possible problem is returning error codes/messages - perhaps we should have an 
errno method too, because the openSSL calls keep the errno per connection.

Can you see any other problems we should consider?

Let me know your thoughts,

--Wez.



--
PHP Development Mailing List <http://www.php.net/>
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
To contact the list administrators, e-mail: [EMAIL PROTECTED]

Reply via email to