1// Copyright 2010, Shuo Chen.  All rights reserved.
2// http://code.google.com/p/muduo/
3//
4// Use of this source code is governed by a BSD-style license
5// that can be found in the License file.
6
7// Author: Shuo Chen (chenshuo at chenshuo dot com)
8
9#include "SocketsOps.h"
10#include "logging/Logging.h"
11
12#include <errno.h>
13#include <fcntl.h>
14#include <stdio.h>  // snprintf
15#include <strings.h>  // bzero
16#include <sys/socket.h>
17#include <unistd.h>
18
19using namespace muduo;
20
21namespace
22{
23
24typedef struct sockaddr SA;
25
26const SA* sockaddr_cast(const struct sockaddr_in* addr)
27{
28  return static_cast<const SA*>(implicit_cast<const void*>(addr));
29}
30
31SA* sockaddr_cast(struct sockaddr_in* addr)
32{
33  return static_cast<SA*>(implicit_cast<void*>(addr));
34}
35
36void setNonBlockAndCloseOnExec(int sockfd)
37{
38  // non-block
39  int flags = ::fcntl(sockfd, F_GETFL, 0);
40  flags |= O_NONBLOCK;
41  int ret = ::fcntl(sockfd, F_SETFL, flags);
42  // FIXME check
43
44  // close-on-exec
45  flags = ::fcntl(sockfd, F_GETFD, 0);
46  flags |= FD_CLOEXEC;
47  ret = ::fcntl(sockfd, F_SETFD, flags);
48  // FIXME check
49}
50
51}
52
53int sockets::createNonblockingOrDie()
54{
55  // socket
56#if VALGRIND
57  int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
58  if (sockfd < 0)
59  {
60    LOG_SYSFATAL << "sockets::createNonblockingOrDie";
61  }
62
63  setNonBlockAndCloseOnExec(sockfd);
64#else
65  int sockfd = ::socket(AF_INET,
66                        SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
67                        IPPROTO_TCP);
68  if (sockfd < 0)
69  {
70    LOG_SYSFATAL << "sockets::createNonblockingOrDie";
71  }
72#endif
73  return sockfd;
74}
75
76void sockets::bindOrDie(int sockfd, const struct sockaddr_in& addr)
77{
78  int ret = ::bind(sockfd, sockaddr_cast(&addr), sizeof addr);
79  if (ret < 0)
80  {
81    LOG_SYSFATAL << "sockets::bindOrDie";
82  }
83}
84
85void sockets::listenOrDie(int sockfd)
86{
87  int ret = ::listen(sockfd, SOMAXCONN);
88  if (ret < 0)
89  {
90    LOG_SYSFATAL << "sockets::listenOrDie";
91  }
92}
93
94int sockets::accept(int sockfd, struct sockaddr_in* addr)
95{
96  socklen_t addrlen = sizeof *addr;
97#if VALGRIND
98  int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);
99  setNonBlockAndCloseOnExec(connfd);
100#else
101  int connfd = ::accept4(sockfd, sockaddr_cast(addr),
102                         &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
103#endif
104  if (connfd < 0)
105  {
106    int savedErrno = errno;
107    LOG_SYSERR << "Socket::accept";
108    switch (savedErrno)
109    {
110      case EAGAIN:
111      case ECONNABORTED:
112      case EINTR:
113      case EPROTO: // ???
114      case EPERM:
115      case EMFILE: // per-process lmit of open file desctiptor ???
116        // expected errors
117        errno = savedErrno;
118        break;
119      case EBADF:
120      case EFAULT:
121      case EINVAL:
122      case ENFILE:
123      case ENOBUFS:
124      case ENOMEM:
125      case ENOTSOCK:
126      case EOPNOTSUPP:
127        // unexpected errors
128        LOG_FATAL << "unexpected error of ::accept " << savedErrno;
129        break;
130      default:
131        LOG_FATAL << "unknown error of ::accept " << savedErrno;
132        break;
133    }
134  }
135  return connfd;
136}
137
138void sockets::close(int sockfd)
139{
140  if (::close(sockfd) < 0)
141  {
142    LOG_SYSERR << "sockets::close";
143  }
144}
145
146void sockets::toHostPort(char* buf, size_t size,
147                         const struct sockaddr_in& addr)
148{
149  char host[INET_ADDRSTRLEN] = "INVALID";
150  ::inet_ntop(AF_INET, &addr.sin_addr, host, sizeof host);
151  uint16_t port = sockets::networkToHost16(addr.sin_port);
152  snprintf(buf, size, "%s:%u", host, port);
153}
154
155void sockets::fromHostPort(const char* ip, uint16_t port,
156                           struct sockaddr_in* addr)
157{
158  addr->sin_family = AF_INET;
159  addr->sin_port = hostToNetwork16(port);
160  if (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0)
161  {
162    LOG_SYSERR << "sockets::fromHostPort";
163  }
164}
165
166struct sockaddr_in sockets::getLocalAddr(int sockfd)
167{
168  struct sockaddr_in localaddr;
169  bzero(&localaddr, sizeof localaddr);
170  socklen_t addrlen = sizeof(localaddr);
171  if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0)
172  {
173    LOG_SYSERR << "sockets::getLocalAddr";
174  }
175  return localaddr;
176}
177
178