]> Git Repo - VerusCoin.git/blob - src/rpcprotocol.cpp
Auto merge of #2093 - bitcartel:sent_network_alerts, r=daira
[VerusCoin.git] / src / rpcprotocol.cpp
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "rpcprotocol.h"
7
8 #include "clientversion.h"
9 #include "random.h"
10 #include "tinyformat.h"
11 #include "util.h"
12 #include "utilstrencodings.h"
13 #include "utiltime.h"
14 #include "version.h"
15
16 #include <stdint.h>
17 #include <fstream>
18
19 #include <boost/algorithm/string.hpp>
20 #include <boost/asio.hpp>
21 #include <boost/asio/ssl.hpp>
22 #include <boost/bind.hpp>
23 #include <boost/filesystem.hpp>
24 #include <boost/foreach.hpp>
25 #include <boost/iostreams/concepts.hpp>
26 #include <boost/iostreams/stream.hpp>
27 #include <boost/shared_ptr.hpp>
28
29 #include <univalue.h>
30
31 using namespace std;
32
33 //! Number of bytes to allocate and read at most at once in post data
34 const size_t POST_READ_SIZE = 256 * 1024;
35
36 /**
37  * HTTP protocol
38  *
39  * This ain't Apache.  We're just using HTTP header for the length field
40  * and to be compatible with other JSON-RPC implementations.
41  */
42
43 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
44 {
45     ostringstream s;
46     s << "POST / HTTP/1.1\r\n"
47       << "User-Agent: zcash-json-rpc/" << FormatFullVersion() << "\r\n"
48       << "Host: 127.0.0.1\r\n"
49       << "Content-Type: application/json\r\n"
50       << "Content-Length: " << strMsg.size() << "\r\n"
51       << "Connection: close\r\n"
52       << "Accept: application/json\r\n";
53     BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
54         s << item.first << ": " << item.second << "\r\n";
55     s << "\r\n" << strMsg;
56
57     return s.str();
58 }
59
60 static string rfc1123Time()
61 {
62     return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
63 }
64
65 static const char *httpStatusDescription(int nStatus)
66 {
67     switch (nStatus) {
68         case HTTP_OK: return "OK";
69         case HTTP_BAD_REQUEST: return "Bad Request";
70         case HTTP_FORBIDDEN: return "Forbidden";
71         case HTTP_NOT_FOUND: return "Not Found";
72         case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
73         default: return "";
74     }
75 }
76
77 string HTTPError(int nStatus, bool keepalive, bool headersOnly)
78 {
79     if (nStatus == HTTP_UNAUTHORIZED)
80         return strprintf("HTTP/1.0 401 Authorization Required\r\n"
81             "Date: %s\r\n"
82             "Server: zcash-json-rpc/%s\r\n"
83             "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
84             "Content-Type: text/html\r\n"
85             "Content-Length: 296\r\n"
86             "\r\n"
87             "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
88             "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
89             "<HTML>\r\n"
90             "<HEAD>\r\n"
91             "<TITLE>Error</TITLE>\r\n"
92             "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
93             "</HEAD>\r\n"
94             "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
95             "</HTML>\r\n", rfc1123Time(), FormatFullVersion());
96
97     return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive,
98                      headersOnly, "text/plain");
99 }
100
101 string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength, const char *contentType)
102 {
103     return strprintf(
104             "HTTP/1.1 %d %s\r\n"
105             "Date: %s\r\n"
106             "Connection: %s\r\n"
107             "Content-Length: %u\r\n"
108             "Content-Type: %s\r\n"
109             "Server: zcash-json-rpc/%s\r\n"
110             "\r\n",
111         nStatus,
112         httpStatusDescription(nStatus),
113         rfc1123Time(),
114         keepalive ? "keep-alive" : "close",
115         contentLength,
116         contentType,
117         FormatFullVersion());
118 }
119
120 string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
121                  bool headersOnly, const char *contentType)
122 {
123     if (headersOnly)
124     {
125         return HTTPReplyHeader(nStatus, keepalive, 0, contentType);
126     } else {
127         return HTTPReplyHeader(nStatus, keepalive, strMsg.size(), contentType) + strMsg;
128     }
129 }
130
131 bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
132                          string& http_method, string& http_uri)
133 {
134     string str;
135     getline(stream, str);
136
137     // HTTP request line is space-delimited
138     vector<string> vWords;
139     boost::split(vWords, str, boost::is_any_of(" "));
140     if (vWords.size() < 2)
141         return false;
142
143     // HTTP methods permitted: GET, POST
144     http_method = vWords[0];
145     if (http_method != "GET" && http_method != "POST")
146         return false;
147
148     // HTTP URI must be an absolute path, relative to current host
149     http_uri = vWords[1];
150     if (http_uri.size() == 0 || http_uri[0] != '/')
151         return false;
152
153     // parse proto, if present
154     string strProto = "";
155     if (vWords.size() > 2)
156         strProto = vWords[2];
157
158     proto = 0;
159     const char *ver = strstr(strProto.c_str(), "HTTP/1.");
160     if (ver != NULL)
161         proto = atoi(ver+7);
162
163     return true;
164 }
165
166 int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
167 {
168     string str;
169     getline(stream, str);
170     vector<string> vWords;
171     boost::split(vWords, str, boost::is_any_of(" "));
172     if (vWords.size() < 2)
173         return HTTP_INTERNAL_SERVER_ERROR;
174     proto = 0;
175     const char *ver = strstr(str.c_str(), "HTTP/1.");
176     if (ver != NULL)
177         proto = atoi(ver+7);
178     return atoi(vWords[1].c_str());
179 }
180
181 int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
182 {
183     int nLen = 0;
184     while (true)
185     {
186         string str;
187         std::getline(stream, str);
188         if (str.empty() || str == "\r")
189             break;
190         string::size_type nColon = str.find(":");
191         if (nColon != string::npos)
192         {
193             string strHeader = str.substr(0, nColon);
194             boost::trim(strHeader);
195             boost::to_lower(strHeader);
196             string strValue = str.substr(nColon+1);
197             boost::trim(strValue);
198             mapHeadersRet[strHeader] = strValue;
199             if (strHeader == "content-length")
200                 nLen = atoi(strValue.c_str());
201         }
202     }
203     return nLen;
204 }
205
206
207 int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
208                     string>& mapHeadersRet, string& strMessageRet,
209                     int nProto, size_t max_size)
210 {
211     mapHeadersRet.clear();
212     strMessageRet = "";
213
214     // Read header
215     int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
216     if (nLen < 0 || (size_t)nLen > max_size)
217         return HTTP_INTERNAL_SERVER_ERROR;
218
219     // Read message
220     if (nLen > 0)
221     {
222         vector<char> vch;
223         size_t ptr = 0;
224         while (ptr < (size_t)nLen)
225         {
226             size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE);
227             vch.resize(ptr + bytes_to_read);
228             stream.read(&vch[ptr], bytes_to_read);
229             if (!stream) // Connection lost while reading
230                 return HTTP_INTERNAL_SERVER_ERROR;
231             ptr += bytes_to_read;
232         }
233         strMessageRet = string(vch.begin(), vch.end());
234     }
235
236     string sConHdr = mapHeadersRet["connection"];
237
238     if ((sConHdr != "close") && (sConHdr != "keep-alive"))
239     {
240         if (nProto >= 1)
241             mapHeadersRet["connection"] = "keep-alive";
242         else
243             mapHeadersRet["connection"] = "close";
244     }
245
246     return HTTP_OK;
247 }
248
249 /**
250  * JSON-RPC protocol.  Bitcoin speaks version 1.0 for maximum compatibility,
251  * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
252  * unspecified (HTTP errors and contents of 'error').
253  *
254  * 1.0 spec: http://json-rpc.org/wiki/specification
255  * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
256  * http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
257  */
258
259 string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id)
260 {
261     UniValue request(UniValue::VOBJ);
262     request.push_back(Pair("method", strMethod));
263     request.push_back(Pair("params", params));
264     request.push_back(Pair("id", id));
265     return request.write() + "\n";
266 }
267
268 UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id)
269 {
270     UniValue reply(UniValue::VOBJ);
271     if (!error.isNull())
272         reply.push_back(Pair("result", NullUniValue));
273     else
274         reply.push_back(Pair("result", result));
275     reply.push_back(Pair("error", error));
276     reply.push_back(Pair("id", id));
277     return reply;
278 }
279
280 string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id)
281 {
282     UniValue reply = JSONRPCReplyObj(result, error, id);
283     return reply.write() + "\n";
284 }
285
286 UniValue JSONRPCError(int code, const string& message)
287 {
288     UniValue error(UniValue::VOBJ);
289     error.push_back(Pair("code", code));
290     error.push_back(Pair("message", message));
291     return error;
292 }
293
294 /** Username used when cookie authentication is in use (arbitrary, only for
295  * recognizability in debugging/logging purposes)
296  */
297 static const std::string COOKIEAUTH_USER = "__cookie__";
298 /** Default name for auth cookie file */
299 static const std::string COOKIEAUTH_FILE = ".cookie";
300
301 boost::filesystem::path GetAuthCookieFile()
302 {
303     boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
304     if (!path.is_complete()) path = GetDataDir() / path;
305     return path;
306 }
307
308 bool GenerateAuthCookie(std::string *cookie_out)
309 {
310     unsigned char rand_pwd[32];
311     GetRandBytes(rand_pwd, 32);
312     std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32);
313
314     /** the umask determines what permissions are used to create this file -
315      * these are set to 077 in init.cpp unless overridden with -sysperms.
316      */
317     std::ofstream file;
318     boost::filesystem::path filepath = GetAuthCookieFile();
319     file.open(filepath.string().c_str());
320     if (!file.is_open()) {
321         LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string());
322         return false;
323     }
324     file << cookie;
325     file.close();
326     LogPrintf("Generated RPC authentication cookie %s\n", filepath.string());
327
328     if (cookie_out)
329         *cookie_out = cookie;
330     return true;
331 }
332
333 bool GetAuthCookie(std::string *cookie_out)
334 {
335     std::ifstream file;
336     std::string cookie;
337     boost::filesystem::path filepath = GetAuthCookieFile();
338     file.open(filepath.string().c_str());
339     if (!file.is_open())
340         return false;
341     std::getline(file, cookie);
342     file.close();
343
344     if (cookie_out)
345         *cookie_out = cookie;
346     return true;
347 }
348
349 void DeleteAuthCookie()
350 {
351     try {
352         boost::filesystem::remove(GetAuthCookieFile());
353     } catch (const boost::filesystem::filesystem_error& e) {
354         LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
355     }
356 }
357
This page took 0.044339 seconds and 4 git commands to generate.