Archived
1
0

update dis shit mang

This commit is contained in:
Jeff Becker 2016-10-15 12:37:59 -04:00
parent cc089b3401
commit d31ca5b6a7
No known key found for this signature in database
GPG Key ID: AB950234D6EA286B
18 changed files with 649 additions and 93 deletions

View File

@ -1,2 +1,4 @@
*.o
nntpchan
nntpd
nntpchan-tool
.gdb_history

View File

@ -1,5 +1,7 @@
EXE = nntpchan
EXE = nntpd
TOOL = nntpchan-tool
CXX = clang++
@ -9,21 +11,24 @@ SOURCES := $(wildcard $(SRC_PATH)/*.cpp)
HEADERS := $(wildcard $(SRC_PATH)/*.hpp)
OBJECTS := $(SOURCES:.cpp=.o)
PKGS := libuv
PKGS := libuv libsodium
LD_FLAGS := $(shell pkg-config --libs $(PKGS))
INC_FLAGS := -I $(SRC_PATH) $(shell pkg-config --cflags $(PKGS))
CXXFLAGS := -std=c++14 -Wall -Wextra $(INC_FLAGS)
CXXFLAGS := -std=c++14 -Wall -Wextra $(INC_FLAGS) -g
all: $(EXE)
all: $(EXE) $(TOOL)
$(EXE): $(OBJECTS)
$(CXX) -o $(EXE) $(LD_FLAGS) $(OBJECTS)
$(EXE): $(OBJECTS)
$(CXX) -o $(EXE) $(LD_FLAGS) $(OBJECTS) $(CXXFLAGS) nntpchan.cpp
$(TOOL): $(OBJECTS)
$(CXX) -o $(TOOL) $(LD_FLAGS) $(OBJECTS) $(CXXFLAGS) tool.cpp
%.o: src/%.cpp
$(CXX) $(CXXFLAGS) -c -o $@
clean:
rm -f $(OBJECTS) $(EXE)
rm -f $(OBJECTS) $(EXE) $(TOOL)

View File

@ -0,0 +1,234 @@
#include "base64.hpp"
// taken from i2pd
namespace i2p
{
namespace data
{
static void iT64Build(void);
/*
*
* BASE64 Substitution Table
* -------------------------
*
* Direct Substitution Table
*/
static const char T64[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
/*
* Reverse Substitution Table (built in run time)
*/
static char iT64[256];
static int isFirstTime = 1;
/*
* Padding
*/
static char P64 = '=';
/*
*
* ByteStreamToBase64
* ------------------
*
* Converts binary encoded data to BASE64 format.
*
*/
static size_t /* Number of bytes in the encoded buffer */
ByteStreamToBase64 (
const uint8_t * InBuffer, /* Input buffer, binary data */
size_t InCount, /* Number of bytes in the input buffer */
char * OutBuffer, /* output buffer */
size_t len /* length of output buffer */
)
{
unsigned char * ps;
unsigned char * pd;
unsigned char acc_1;
unsigned char acc_2;
int i;
int n;
int m;
size_t outCount;
ps = (unsigned char *)InBuffer;
n = InCount/3;
m = InCount%3;
if (!m)
outCount = 4*n;
else
outCount = 4*(n+1);
if (outCount > len) return 0;
pd = (unsigned char *)OutBuffer;
for ( i = 0; i<n; i++ ){
acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x30;
acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1];
acc_1 = *ps++;
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
*pd++ = T64[acc_2];
acc_1 &= 0x0f;
acc_1 <<=2;
acc_2 = *ps++;
acc_1 |= acc_2>>6; /* base64 digit #3 */
*pd++ = T64[acc_1];
acc_2 &= 0x3f; /* base64 digit #4 */
*pd++ = T64[acc_2];
}
if ( m == 1 ){
acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x3f; /* base64 digit #2 */
acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1];
*pd++ = T64[acc_2];
*pd++ = P64;
*pd++ = P64;
}
else if ( m == 2 ){
acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x3f;
acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1];
acc_1 = *ps++;
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
*pd++ = T64[acc_2];
acc_1 &= 0x0f;
acc_1 <<=2; /* base64 digit #3 */
*pd++ = T64[acc_1];
*pd++ = P64;
}
return outCount;
}
/*
*
* Base64ToByteStream
* ------------------
*
* Converts BASE64 encoded data to binary format. If input buffer is
* not properly padded, buffer of negative length is returned
*
*/
static
ssize_t /* Number of output bytes */
Base64ToByteStream (
const char * InBuffer, /* BASE64 encoded buffer */
size_t InCount, /* Number of input bytes */
uint8_t * OutBuffer, /* output buffer length */
size_t len /* length of output buffer */
)
{
unsigned char * ps;
unsigned char * pd;
unsigned char acc_1;
unsigned char acc_2;
int i;
int n;
int m;
size_t outCount;
if (isFirstTime) iT64Build();
n = InCount/4;
m = InCount%4;
if (InCount && !m)
outCount = 3*n;
else {
outCount = 0;
return 0;
}
ps = (unsigned char *)(InBuffer + InCount - 1);
while ( *ps-- == P64 ) outCount--;
ps = (unsigned char *)InBuffer;
if (outCount > len) return -1;
pd = OutBuffer;
auto endOfOutBuffer = OutBuffer + outCount;
for ( i = 0; i < n; i++ ){
acc_1 = iT64[*ps++];
acc_2 = iT64[*ps++];
acc_1 <<= 2;
acc_1 |= acc_2>>4;
*pd++ = acc_1;
if (pd >= endOfOutBuffer) break;
acc_2 <<= 4;
acc_1 = iT64[*ps++];
acc_2 |= acc_1 >> 2;
*pd++ = acc_2;
if (pd >= endOfOutBuffer) break;
acc_2 = iT64[*ps++];
acc_2 |= acc_1 << 6;
*pd++ = acc_2;
}
return outCount;
}
static size_t Base64EncodingBufferSize (const size_t input_size)
{
auto d = div (input_size, 3);
if (d.rem) d.quot++;
return 4*d.quot;
}
/*
*
* iT64
* ----
* Reverse table builder. P64 character is replaced with 0
*
*
*/
static void iT64Build()
{
int i;
isFirstTime = 0;
for ( i=0; i<256; i++ ) iT64[i] = -1;
for ( i=0; i<64; i++ ) iT64[(int)T64[i]] = i;
iT64[(int)P64] = 0;
}
}
}
namespace nntpchan
{
std::string B64Encode(const uint8_t * data, const std::size_t l)
{
std::string out;
out.reserve(i2p::data::Base64EncodingBufferSize(l));
i2p::data::ByteStreamToBase64(data, l, &out[0], out.capacity());
return out;
}
bool B64Decode(const std::string & data, std::vector<uint8_t> & out)
{
out.resize(data.size());
if(i2p::data::Base64ToByteStream(data.c_str(), data.size(), &out[0], out.size()) == -1) return false;
out.shrink_to_fit();
return true;
}
}

View File

@ -0,0 +1,17 @@
#ifndef NNTPCHAN_BASE64_HPP
#define NNTPCHAN_BASE64_HPP
#include <string>
#include <vector>
namespace nntpchan
{
/** returns base64 encoded string */
std::string B64Encode(const uint8_t * data, const std::size_t l);
/** @brief returns true if decode was successful */
bool B64Decode(const std::string & data, std::vector<uint8_t> & out);
}
#endif

View File

@ -0,0 +1,9 @@
#include "crypto.hpp"
namespace nntpchan
{
void SHA512(const uint8_t * d, const std::size_t l, SHA512Digest & h)
{
crypto_hash(h.data(), d, l);
}
}

View File

@ -0,0 +1,15 @@
#ifndef NNTPCHAN_CRYPTO_HPP
#define NNTPCHAN_CRYPTO_HPP
#include <sodium/crypto_hash.h>
#include <array>
namespace nntpchan
{
typedef std::array<uint8_t, crypto_hash_BYTES> SHA512Digest;
void SHA512(const uint8_t * d, std::size_t l, SHA512Digest & h);
}
#endif

View File

@ -0,0 +1,41 @@
#include "line.hpp"
#include <iostream>
namespace nntpchan {
void LineReader::OnData(const char * d, ssize_t l)
{
if(l <= 0) return;
std::size_t idx = 0;
while(l-- > 0) {
char c = d[idx++];
if(c == '\n') {
OnLine(d, idx-2);
d += idx;
}
}
}
void LineReader::OnLine(const char *d, const size_t l)
{
std::string line(d, l);
HandleLine(line);
}
bool LineReader::HasNextLine()
{
return m_sendlines.size() > 0;
}
std::string LineReader::GetNextLine()
{
std::string line = m_sendlines[0];
m_sendlines.pop_front();
return line;
}
void LineReader::QueueLine(const std::string & line)
{
m_sendlines.push_back(line);
}
}

View File

@ -0,0 +1,35 @@
#ifndef NNTPCHAN_LINE_HPP
#define NNTPCHAN_LINE_HPP
#include <string>
#include <deque>
namespace nntpchan
{
/** @brief a buffered line reader */
class LineReader
{
public:
/** @brief queue inbound data from connection */
void OnData(const char * data, ssize_t s);
/** @brief do we have line to send to the client? */
bool HasNextLine();
/** @brief get the next line to send to the client, does not check if it exists */
std::string GetNextLine();
protected:
/** @brief handle a line from the client */
virtual void HandleLine(const std::string & line) = 0;
/** @brief queue the next line to send to the client */
void QueueLine(const std::string & line);
private:
void OnLine(const char * d, const size_t l);
// lines to send
std::deque<std::string> m_sendlines;
};
}
#endif

View File

@ -0,0 +1,61 @@
#include "nntp_auth.hpp"
#include "crypto.hpp"
#include "base64.hpp"
#include <array>
#include <iostream>
#include <fstream>
namespace nntpchan
{
HashedCredDB::HashedCredDB(std::istream & i) :
m_instream(i) {}
bool HashedCredDB::CheckLogin(const std::string & user, const std::string & passwd)
{
std::unique_lock<std::mutex> lock(m_access);
m_found = false;
m_user = user;
m_passwd = passwd;
m_instream.seekg(0, std::ios::end);
const auto l = m_instream.tellg();
m_instream.seekg(0, std::ios::beg);
char * buff = new char[l];
// read file
m_instream.read(buff, l);
OnData(buff, l);
delete [] buff;
return m_found;
}
bool HashedCredDB::ProcessLine(const std::string & line)
{
// strip comments
auto comment = line.find("#");
std::string part = line;
for (; comment != std::string::npos; comment = part.find("#")) {
if(comment)
part = part.substr(0, comment);
else break;
}
if(!part.size()) return false; // empty line after comments
auto idx = part.find(":");
if (idx == std::string::npos) return false; // bad format
if (m_user != part.substr(0, idx)) return false; // username mismatch
part = part.substr(idx+1);
idx = part.find(":");
if (idx == std::string::npos) return false; // bad format
std::string cred = part.substr(0, idx);
std::string salt = part.substr(idx+1);
return Hash(m_passwd, salt) == cred;
}
std::string HashedCredDB::Hash(const std::string & data, const std::string & salt)
{
SHA512Digest h;
std::string d = data + salt;
SHA512((const uint8_t*)d.c_str(), d.size(), h);
return B64Encode(h.data(), h.size());
}
}

View File

@ -0,0 +1,38 @@
#ifndef NNTPCHAN_NNTP_AUTH_HPP
#define NNTPCHAN_NNTP_AUTH_HPP
#include <string>
#include <iostream>
#include <mutex>
#include "line.hpp"
namespace nntpchan
{
/** @brief nntp credential db interface */
class NNTPCredentialDB
{
public:
/** @brief return true if username password combo is correct */
virtual bool CheckLogin(const std::string & user, const std::string & passwd) = 0;
};
/** @brief nntp credential db using hashed+salted passwords */
class HashedCredDB : public NNTPCredentialDB, public LineReader
{
public:
HashedCredDB(std::istream & i);
bool CheckLogin(const std::string & user, const std::string & passwd);
protected:
std::string Hash(const std::string & data, const std::string & salt);
private:
bool ProcessLine(const std::string & line);
std::mutex m_access;
std::string m_user, m_passwd;
bool m_found;
/** return true if we have a line that matches this username / password combo */
std::istream & m_instream;
};
}
#endif

View File

@ -0,0 +1,74 @@
#include "nntp_handler.hpp"
#include <algorithm>
#include <cctype>
#include <string>
#include <sstream>
#include <iostream>
namespace nntpchan
{
NNTPServerHandler::NNTPServerHandler(const std::string & storage) :
m_store(storage),
m_authed(false),
m_state(eStateReadCommand)
{
}
void NNTPServerHandler::HandleLine(const std::string &line)
{
if(m_state == eStateReadCommand) {
std::deque<std::string> command;
std::istringstream s;
s.str(line);
for (std::string part; std::getline(s, part, ' '); ) {
if(part.size()) command.push_back(std::string(part));
}
if(command.size())
HandleCommand(command);
else
QueueLine("501 Syntax error");
}
}
void NNTPServerHandler::HandleCommand(const std::deque<std::string> & command)
{
auto cmd = command[0];
std::transform(cmd.begin(), cmd.end(), cmd.begin(),
[](unsigned char ch) { return std::toupper(ch); });
std::size_t cmdlen = command.size();
std::cerr << "handle command [" << cmd << "] " << (int) cmdlen << std::endl;
if (cmd == "QUIT") {
Quit();
return;
} else if (cmd == "MODE" ) {
if(cmdlen == 1) {
// set mode
SwitchMode(command[1]);
} else if(cmdlen) {
// too many arguments
} else {
// get mode
}
} else {
// unknown command
QueueLine("500 Unknown Command");
}
}
void NNTPServerHandler::SwitchMode(const std::string & mode)
{
}
void NNTPServerHandler::Quit()
{
std::cerr << "quitting" << std::endl;
m_state = eStateQuit;
QueueLine("205 quitting");
}
bool NNTPServerHandler::Done()
{
return m_state == eStateQuit;
}
}

View File

@ -0,0 +1,45 @@
#ifndef NNTPCHAN_NNTP_HANDLER_HPP
#define NNTPCHAN_NNTP_HANDLER_HPP
#include <deque>
#include <string>
#include "line.hpp"
#include "storage.hpp"
namespace nntpchan
{
class NNTPServerHandler : public LineReader
{
public:
NNTPServerHandler(const std::string & storage);
bool Done();
protected:
void HandleLine(const std::string & line);
void HandleCommand(const std::deque<std::string> & command);
private:
enum State {
eStateReadCommand,
eStateStoreArticle,
eStateQuit
};
private:
// handle quit command, this queues a reply
void Quit();
// switch nntp modes, this queues a reply
void SwitchMode(const std::string & mode);
private:
ArticleStorage m_store;
std::string m_mode;
bool m_authed;
State m_state;
};
}
#endif

View File

@ -39,7 +39,7 @@ namespace nntpchan
return;
}
NNTPServerConn * conn = new NNTPServerConn(m_loop, s, m_storagePath);
conn->SendCode(200, "Posting Allowed");
conn->Greet();
}
@ -56,6 +56,7 @@ namespace nntpchan
uv_accept(s, (uv_stream_t*) &m_conn);
uv_read_start((uv_stream_t*) &m_conn, [] (uv_handle_t * h, size_t s, uv_buf_t * b) {
NNTPServerConn * self = (NNTPServerConn*) h->data;
if(self == nullptr) return;
b->base = self->m_readbuff;
if (s > sizeof(self->m_readbuff))
b->len = sizeof(self->m_readbuff);
@ -63,70 +64,56 @@ namespace nntpchan
b->len = s;
}, [] (uv_stream_t * s, ssize_t nread, const uv_buf_t * b) {
NNTPServerConn * self = (NNTPServerConn*) s->data;
if(self == nullptr) return;
if(nread > 0) {
self->ProcessData(b->base, nread);
self->m_handler.OnData(b->base, nread);
self->SendNextReply();
if(self->m_handler.Done())
self->Close();
} else {
if (nread != UV_EOF) {
std::cerr << "error in nntp server conn alloc: ";
std::cerr << uv_strerror(nread);
std::cerr << std::endl;
}
delete self;
s->data = nullptr;
// got eof or error
self->Close();
}
});
}
NNTPServerConn::~NNTPServerConn()
{
uv_close((uv_handle_t*)&m_conn, [] (uv_handle_t *) {});
}
void NNTPServerConn::ProcessData(const char *d, ssize_t l)
{
m_handler.OnData(d, l);
}
void NNTPServerConn::SendNextReply()
{
if(m_handler.HasNextLine()) {
auto line = m_handler.GetNextLine();
SendLine(line);
SendString(line+"\n");
}
}
void NNTPServerConn::SendCode(const int code, const std::string & msg)
{
std::stringstream ss;
ss << code << " " << msg << std::endl;
SendString(ss.str());
}
void NNTPServerConn::SendString(const std::string & line)
{
WriteBuffer * b = new WriteBuffer(line);
uv_write(&b->w, *this, &b->b, 1, [](uv_write_t * w, int status) {
WriteBuffer * wb = (WriteBuffer *) w->data;
delete wb;
});
}
NNTPServerHandler::NNTPServerHandler(const std::string & storagepath) :
m_state(eNNTPStateGreet)
{
m_storage.SetPath(storagepath);
}
NNTPServerHandler::~NNTPServerHandler()
{
}
void NNTPServerHandler::OnData(const char * d, ssize_t l)
void NNTPServerConn::Greet()
{
}
void NNTPServerConn::SendString(const std::string & str)
{
WriteBuffer * b = new WriteBuffer(str);
uv_write(&b->w, *this, &b->b, 1, [](uv_write_t * w, int status) {
(void) status;
WriteBuffer * wb = (WriteBuffer *) w->data;
if(wb)
delete wb;
});
}
void NNTPServerConn::Close()
{
uv_close((uv_handle_t*)&m_conn, [] (uv_handle_t * s) {
NNTPServerConn * self = (NNTPServerConn*) s->data;
if(self)
delete self;
s->data = nullptr;
});
}
}

View File

@ -2,12 +2,14 @@
#define NNTPCHAN_NNTP_SERVER_HPP
#include <uv.h>
#include <string>
#include <vector>
#include <deque>
#include "storage.hpp"
#include "nntp_auth.hpp"
#include "nntp_handler.hpp"
namespace nntpchan
{
class NNTPServerConn;
class NNTPServer
@ -31,58 +33,24 @@ namespace nntpchan
uv_tcp_t m_server;
uv_loop_t * m_loop;
std::vector<NNTPServerConn *> m_conns;
std::deque<NNTPServerConn *> m_conns;
std::string m_storagePath;
};
class NNTPServerHandler
{
public:
enum State {
eNNTPStateGreet,
eNNTPStateHandshake,
eNNTPStateReader,
eNNTPStateStream,
eNNTPStateTAKETHIS,
eNNTPStateIHAVE,
eNNTPStateARTICLE,
eNNTPStatePOST
};
NNTPServerHandler(const std::string & storagepath);
~NNTPServerHandler();
void OnData(const char * data, ssize_t s);
bool HasNextLine();
std::string GetNextLine();
private:
State m_state;
ArticleStorage m_storage;
};
class NNTPServerConn
{
public:
NNTPServerConn(uv_loop_t * l, uv_stream_t * s, const std::string & storage);
virtual ~NNTPServerConn();
/** @brief close connection, this connection cannot be used after calling this */
void Close();
void Quit();
void SendLine(const std::string & line);
void SendCode(const int code, const std::string & message);
void ProcessData(const char * d, ssize_t l);
/** @brief send next queued reply */
void SendNextReply();
void Greet();
private:
@ -94,8 +62,10 @@ namespace nntpchan
uv_tcp_t m_conn;
NNTPServerHandler m_handler;
NNTPCredentialDB * m_logindb;
char m_readbuff[1024];
char m_readbuff[1028];
};
}

View File

@ -9,6 +9,10 @@ namespace nntpchan
{
}
ArticleStorage::ArticleStorage(const std::string & fpath) {
SetPath(fpath);
}
ArticleStorage::~ArticleStorage()
{
}

View File

@ -10,6 +10,7 @@ namespace nntpchan
{
public:
ArticleStorage();
ArticleStorage(const std::string & fpath);
~ArticleStorage();
void SetPath(const std::string & fpath);

View File

@ -0,0 +1,18 @@
#include "base64.hpp"
#include "crypto.hpp"
#include <string>
#include <iostream>
static void print_help(const std::string & exename)
{
std::cout << "usage: " << exename << " [help|passwd|genconf]" << std::endl;
}
int main(int argc, char * argv[])
{
if(argc == 1) {
print_help(argv[0]);
return 1;
}
}