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
76int sockets::connect(int sockfd, const struct sockaddr_in& addr)
77{
78  return ::connect(sockfd, sockaddr_cast(&addr), sizeof addr);
79}
80
81void sockets::bindOrDie(int sockfd, const struct sockaddr_in& addr)
82{
83  int ret = ::bind(sockfd, sockaddr_cast(&addr), sizeof addr);
84  if (ret < 0)
85  {
86    LOG_SYSFATAL << "sockets::bindOrDie";
87  }
88}
89
90void sockets::listenOrDie(int sockfd)
91{
92  int ret = ::listen(sockfd, SOMAXCONN);
93  if (ret < 0)
94  {
95    LOG_SYSFATAL << "sockets::listenOrDie";
96  }
97}
98
99int sockets::accept(int sockfd, struct sockaddr_in* addr)
100{
101  socklen_t addrlen = sizeof *addr;
102#if VALGRIND
103  int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);
104  setNonBlockAndCloseOnExec(connfd);
105#else
106  int connfd = ::accept4(sockfd, sockaddr_cast(addr),
107                         &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
108#endif
109  if (connfd < 0)
110  {
111    int savedErrno = errno;
112    LOG_SYSERR << "Socket::accept";
113    switch (savedErrno)
114    {
115      case EAGAIN:
116      case ECONNABORTED:
117      case EINTR:
118      case EPROTO: // ???
119      case EPERM:
120      case EMFILE: // per-process lmit of open file desctiptor ???
121        // expected errors
122        errno = savedErrno;
123        break;
124      case EBADF:
125      case EFAULT:
126      case EINVAL:
127      case ENFILE:
128      case ENOBUFS:
129      case ENOMEM:
130      case ENOTSOCK:
131      case EOPNOTSUPP:
132        // unexpected errors
133        LOG_FATAL << "unexpected error of ::accept " << savedErrno;
134        break;
135      default:
136        LOG_FATAL << "unknown error of ::accept " << savedErrno;
137        break;
138    }
139  }
140  return connfd;
141}
142
143void sockets::close(int sockfd)
144{
145  if (::close(sockfd) < 0)
146  {
147    LOG_SYSERR << "sockets::close";
148  }
149}
150
151void sockets::shutdownWrite(int sockfd)
152{
153  if (::shutdown(sockfd, SHUT_WR) < 0)
154  {
155    LOG_SYSERR << "sockets::shutdownWrite";
156  }
157}
158
159void sockets::toHostPort(char* buf, size_t size,
160                         const struct sockaddr_in& addr)
161{
162  char host[INET_ADDRSTRLEN] = "INVALID";
163  ::inet_ntop(AF_INET, &addr.sin_addr, host, sizeof host);
164  uint16_t port = sockets::networkToHost16(addr.sin_port);
165  snprintf(buf, size, "%s:%u", host, port);
166}
167
168void sockets::fromHostPort(const char* ip, uint16_t port,
169                           struct sockaddr_in* addr)
170{
171  addr->sin_family = AF_INET;
172  addr->sin_port = hostToNetwork16(port);
173  if (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0)
174  {
175    LOG_SYSERR << "sockets::fromHostPort";
176  }
177}
178
179struct sockaddr_in sockets::getLocalAddr(int sockfd)
180{
181  struct sockaddr_in localaddr;
182  bzero(&localaddr, sizeof localaddr);
183  socklen_t addrlen = sizeof(localaddr);
184  if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0)
185  {
186    LOG_SYSERR << "sockets::getLocalAddr";
187  }
188  return localaddr;
189}
190
191struct sockaddr_in sockets::getPeerAddr(int sockfd)
192{
193  struct sockaddr_in peeraddr;
194  bzero(&peeraddr, sizeof peeraddr);
195  socklen_t addrlen = sizeof(peeraddr);
196  if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen) < 0)
197  {
198    LOG_SYSERR << "sockets::getPeerAddr";
199  }
200  return peeraddr;
201}
202
203int sockets::getSocketError(int sockfd)
204{
205  int optval;
206  socklen_t optlen = sizeof optval;
207
208  if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
209  {
210    return errno;
211  }
212  else
213  {
214    return optval;
215  }
216}
217
218bool sockets::isSelfConnect(int sockfd)
219{
220  struct sockaddr_in localaddr = getLocalAddr(sockfd);
221  struct sockaddr_in peeraddr = getPeerAddr(sockfd);
222  return localaddr.sin_port == peeraddr.sin_port
223      && localaddr.sin_addr.s_addr == peeraddr.sin_addr.s_addr;
224}
225