-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathws-util.cpp
More file actions
189 lines (164 loc) · 7.67 KB
/
ws-util.cpp
File metadata and controls
189 lines (164 loc) · 7.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/***********************************************************************
ws-util.cpp - Some basic Winsock utility functions.
This program is hereby released into the public domain. There is
ABSOLUTELY NO WARRANTY WHATSOEVER for this product. Caveat hacker.
***********************************************************************/
#include "ws-util.h"
#include "Logger.hpp"
#include <iostream>
#include <sstream>
#include <algorithm> // for lower_bound()
using namespace std;
#if !defined(_WINSOCK2API_)
// Winsock 2 header defines this, but Winsock 1.1 header doesn't. In
// the interest of not requiring the Winsock 2 SDK which we don't really
// need, we'll just define this one constant ourselves.
#define SD_SEND 1
#endif
//// Statics ///////////////////////////////////////////////////////////
// List of Winsock error constants mapped to an interpretation string.
// Note that this list must remain sorted by the error constants'
// values, because we do a binary search on the list when looking up
// items.
static struct ErrorEntry {
int nID;
const char *pcMessage;
ErrorEntry(int id, const char *pc = 0) :
nID(id),
pcMessage(pc) {}
bool operator<(const ErrorEntry& rhs) const {
return nID < rhs.nID;
}
} g_ErrorList[] = {
ErrorEntry(0, "No error"),
ErrorEntry(WSAEINTR, "Interrupted system call"),
ErrorEntry(WSAEBADF, "Bad file number"),
ErrorEntry(WSAEACCES, "Permission denied"),
ErrorEntry(WSAEFAULT, "Bad address"),
ErrorEntry(WSAEINVAL, "Invalid argument"),
ErrorEntry(WSAEMFILE, "Too many open sockets"),
ErrorEntry(WSAEWOULDBLOCK, "Operation would block"),
ErrorEntry(WSAEINPROGRESS, "Operation now in progress"),
ErrorEntry(WSAEALREADY, "Operation already in progress"),
ErrorEntry(WSAENOTSOCK, "Socket operation on non-socket"),
ErrorEntry(WSAEDESTADDRREQ, "Destination address required"),
ErrorEntry(WSAEMSGSIZE, "Message too long"),
ErrorEntry(WSAEPROTOTYPE, "Protocol wrong type for socket"),
ErrorEntry(WSAENOPROTOOPT, "Bad protocol option"),
ErrorEntry(WSAEPROTONOSUPPORT, "Protocol not supported"),
ErrorEntry(WSAESOCKTNOSUPPORT, "Socket type not supported"),
ErrorEntry(WSAEOPNOTSUPP, "Operation not supported on socket"),
ErrorEntry(WSAEPFNOSUPPORT, "Protocol family not supported"),
ErrorEntry(WSAEAFNOSUPPORT, "Address family not supported"),
ErrorEntry(WSAEADDRINUSE, "Address already in use"),
ErrorEntry(WSAEADDRNOTAVAIL, "Can't assign requested address"),
ErrorEntry(WSAENETDOWN, "Network is down"),
ErrorEntry(WSAENETUNREACH, "Network is unreachable"),
ErrorEntry(WSAENETRESET, "Net connection reset"),
ErrorEntry(WSAECONNABORTED, "Software caused connection abort"),
ErrorEntry(WSAECONNRESET, "Connection reset by peer"),
ErrorEntry(WSAENOBUFS, "No buffer space available"),
ErrorEntry(WSAEISCONN, "Socket is already connected"),
ErrorEntry(WSAENOTCONN, "Socket is not connected"),
ErrorEntry(WSAESHUTDOWN, "Can't send after socket shutdown"),
ErrorEntry(WSAETOOMANYREFS, "Too many references, can't splice"),
ErrorEntry(WSAETIMEDOUT, "Connection timed out"),
ErrorEntry(WSAECONNREFUSED, "Connection refused"),
ErrorEntry(WSAELOOP, "Too many levels of symbolic links"),
ErrorEntry(WSAENAMETOOLONG, "File name too long"),
ErrorEntry(WSAEHOSTDOWN, "Host is down"),
ErrorEntry(WSAEHOSTUNREACH, "No route to host"),
ErrorEntry(WSAENOTEMPTY, "Directory not empty"),
ErrorEntry(WSAEPROCLIM, "Too many processes"),
ErrorEntry(WSAEUSERS, "Too many users"),
ErrorEntry(WSAEDQUOT, "Disc quota exceeded"),
ErrorEntry(WSAESTALE, "Stale NFS file handle"),
ErrorEntry(WSAEREMOTE, "Too many levels of remote in path"),
ErrorEntry(WSASYSNOTREADY, "Network system is unavailable"),
ErrorEntry(WSAVERNOTSUPPORTED, "Winsock version out of range"),
ErrorEntry(WSANOTINITIALISED, "WSAStartup not yet called"),
ErrorEntry(WSAEDISCON, "Graceful shutdown in progress"),
ErrorEntry(WSAHOST_NOT_FOUND, "Host not found"),
ErrorEntry(WSANO_DATA, "No host data of that type was found")
};
const int kNumMessages = sizeof(g_ErrorList) / sizeof(ErrorEntry);
//// WSAGetLastErrorMessage ////////////////////////////////////////////
// A function similar in spirit to Unix's perror() that tacks a canned
// interpretation of the value of WSAGetLastError() onto the end of a
// passed string, separated by a ": ". Generally, you should implement
// smarter error handling than this, but for default cases and simple
// programs, this function is sufficient.
//
// This function returns a pointer to an internal static buffer, so you
// must copy the data from this function before you call it again. It
// follows that this function is also not thread-safe.
string WSAGetLastErrorMessage(const char *pcMessagePrefix, int nErrorID) {
// Build basic error string
ostringstream ss;
ss << pcMessagePrefix << ": ";
// Tack appropriate canned message onto end of supplied message
// prefix. Note that we do a binary search here: g_ErrorList must be
// sorted by the error constant's value.
ErrorEntry *pEnd = g_ErrorList + kNumMessages;
ErrorEntry Target(nErrorID ? nErrorID : WSAGetLastError());
ErrorEntry *it = lower_bound(g_ErrorList, pEnd, Target);
if ((it != pEnd) && (it->nID == Target.nID)) {
ss << it->pcMessage;
}
else {
// Didn't find error in list, so make up a generic one
ss << "unknown error";
}
ss << " (" << Target.nID << ")";
// Finish error message off and return it.
return ss.str();
}
//// ShutdownConnection ////////////////////////////////////////////////
// Gracefully shuts the connection sd down. Returns true if we're
// successful, false otherwise.
bool ShutdownConnection(SOCKET sd, bool rx) {
// Disallow any further data sends. This will tell the other side
// that we want to go away now. If we skip this step, we don't
// shut the connection down nicely.
if (shutdown(sd, SD_SEND) == SOCKET_ERROR) {
auto fmt = __FUNC__ "shutdown() failed";
Logger::LogError(WSAGetLastErrorMessage(fmt));
}
// Receive any extra data still sitting on the socket. After all
// data is received, this call will block until the remote host
// acknowledges the TCP control packet sent by the shutdown above.
// Then we'll get a 0 back from recv, signalling that the remote
// host has closed its side of the connection.
if (rx) {
char acReadBuffer[kBufferSize];
int nTotalRx = 0;
while (true) {
int nNewBytes = recv(sd, acReadBuffer, kBufferSize, 0);
if (nNewBytes == SOCKET_ERROR) {
auto fmt = __FUNC__ "recv() failed";
Logger::LogError(WSAGetLastErrorMessage(fmt));
break;
}
else if (nNewBytes != 0) {
nTotalRx += nNewBytes;
}
else {
// Okay, we're done!
break;
}
}
if (nTotalRx > 0) {
ostringstream ss;
ss << "FYI, received " << nTotalRx << ' ' <<
"unexpected bytes during shutdown";
Logger::LogError(ss.str());
}
}
// Close the socket.
if (closesocket(sd) == SOCKET_ERROR) {
auto fmt = __FUNC__ "closesocket() failed";
Logger::LogError(WSAGetLastErrorMessage(fmt));
return false;
}
return true;
}