From 9264e1070127807d4f765f433f8fa5b15cd8820b Mon Sep 17 00:00:00 2001 From: Nicholas Wang <7414442+nicholascw@users.noreply.github.com> Date: Sun, 27 Jun 2021 16:39:44 -0500 Subject: [PATCH 1/3] renaming in readme.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f460cb0..84c9f04 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# monyhar-lite -梦弘浏览器 自主研发版本 - 完全自主研发,打破国外垄断,比 Chrome 快 600%。缺少上网功能。 +# monyhar-greasy +梦弘浏览器 油腻加重版本 - 完全自主研发,打破国外垄断,比 monyhar-lite 慢 600%。有史前上网功能。 From ee5b91a6213599035ec66110f3f80604c2abe50e Mon Sep 17 00:00:00 2001 From: Nicholas Wang <7414442+nicholascw@users.noreply.github.com> Date: Tue, 29 Jun 2021 13:20:19 -0500 Subject: [PATCH 2/3] 1st version working with HTTP 1.1. --- .gitignore | 3 + Makefile | 26 +++ dbg.h | 561 +++++++++++++++++++++++++++++++++++++++++++++++++++++ log.c | 75 +++++++ log.h | 60 ++++++ main.c | 93 ++++++++- parser.c | 50 +++++ parser.h | 13 ++ stream.c | 174 +++++++++++++++++ stream.h | 15 ++ 10 files changed, 1065 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 dbg.h create mode 100644 log.c create mode 100644 log.h create mode 100644 parser.c create mode 100644 parser.h create mode 100644 stream.c create mode 100644 stream.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0543fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +obj +obj/*.o +monyhar-greasy diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d832695 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CFLAGS= -I. -g3 -O3 -Wall -Wextra -Wno-unused-variable -D _GNU_SOURCE +LIBS= +SRC=$(wildcard *.c) +HDR=$(wildcard *.h) +OBJS=$(patsubst %.c, obj/%.o, $(SRC)) + +.PHONY: all clean + +all: clean obj monyhar-greasy format + +format: + $(foreach n, $(SRC), clang-format -style=google -i $(n); ) + $(foreach n, $(HDR), clang-format -style=google -i $(n); ) + +monyhar-greasy: $(OBJS) + $(CC) $(CFLAGS) $^ -o $@ $(LIBS) + +clean: + $(RM) -r obj monyhar-greasy + +obj/%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +obj: + mkdir -p obj + diff --git a/dbg.h b/dbg.h new file mode 100644 index 0000000..d6bf3a4 --- /dev/null +++ b/dbg.h @@ -0,0 +1,561 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Erik Moqvist + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This file is part of the dbg-macro project. + */ + +#ifndef DBG_H +#define DBG_H + +#include +#include +#include +#include +#include +#ifdef __GLIBC__ +#include +#endif +#include +#include + +/* Library version. */ +#define DBG_VERSION "0.12.1" + +#ifndef NDBG +/** + * For primitive data types (int, float, etc.), strings and + * pointers. Returns the result of given expression. + */ +#define dbg(expr) \ + _Generic((expr), \ + char: dbg_char, \ + signed char: dbg_schar, \ + unsigned char: dbg_uchar, \ + short: dbg_short, \ + unsigned short: dbg_ushort, \ + int: dbg_int, \ + unsigned int: dbg_uint, \ + long: dbg_long, \ + unsigned long: dbg_ulong, \ + long long: dbg_llong, \ + unsigned long long: dbg_ullong, \ + float: dbg_float, \ + double: dbg_double, \ + bool: dbg_bool, \ + char *: dbg_char_p, \ + const char *: dbg_const_char_p, \ + signed char *: dbg_schar_p, \ + const signed char *: dbg_const_schar_p, \ + unsigned char *: dbg_uchar_p, \ + const unsigned char *: dbg_const_uchar_p, \ + default: dbg_pointer) \ + (__FILE__, __LINE__, __func__, #expr, expr) + +/** + * For array of primitive data types. Returns the result of given + * expression. + */ +#define dbga(expr, length) \ + _Generic((expr), \ + short *: dbg_short_p, \ + const short *: dbg_const_short_p, \ + unsigned short *: dbg_ushort_p, \ + const unsigned short *: dbg_const_ushort_p, \ + int *: dbg_int_p, \ + const int *: dbg_const_int_p, \ + unsigned int *: dbg_uint_p, \ + const unsigned int *: dbg_const_uint_p, \ + long *: dbg_long_p, \ + const long *: dbg_const_long_p, \ + unsigned long *: dbg_ulong_p, \ + const unsigned long *: dbg_const_ulong_p, \ + long long *: dbg_llong_p, \ + const long long *: dbg_const_llong_p, \ + unsigned long long *: dbg_ullong_p, \ + const unsigned long long *: dbg_const_ullong_p, \ + float *: dbg_float_p, \ + const float *: dbg_const_float_p, \ + double *: dbg_double_p, \ + const double *: dbg_const_double_p, \ + bool *: dbg_bool_p, \ + const bool *: dbg_const_bool_p) \ + (__FILE__, __LINE__, __func__, #expr, expr, length) + +/** + * To force boolean true/false output. Returns the result of given + * expression. + */ +#define dbgb(expr) dbg_bool(__FILE__, __LINE__, __func__, #expr, expr) + +/** + * Hexdump of given size at given address. Returns the result of given + * expression. + */ +#define dbgh(expr, size) \ + _Generic((expr), \ + char *: dbg_hexdump_char_p, \ + const char *: dbg_hexdump_const_char_p, \ + signed char *: dbg_hexdump_schar_p, \ + const signed char *: dbg_hexdump_const_schar_p, \ + unsigned char *: dbg_hexdump_uchar_p, \ + const unsigned char *: dbg_hexdump_const_uchar_p, \ + short *: dbg_hexdump_short_p, \ + const short *: dbg_hexdump_const_short_p, \ + unsigned short *: dbg_hexdump_ushort_p, \ + const unsigned short *: dbg_hexdump_const_ushort_p, \ + int *: dbg_hexdump_int_p, \ + const int *: dbg_hexdump_const_int_p, \ + unsigned int *: dbg_hexdump_uint_p, \ + const unsigned int *: dbg_hexdump_const_uint_p, \ + long *: dbg_hexdump_long_p, \ + const long *: dbg_hexdump_const_long_p, \ + unsigned long *: dbg_hexdump_ulong_p, \ + const unsigned long *: dbg_hexdump_const_ulong_p, \ + long long *: dbg_hexdump_llong_p, \ + const long long *: dbg_hexdump_const_llong_p, \ + unsigned long long *: dbg_hexdump_ullong_p, \ + const unsigned long long *: dbg_hexdump_const_ullong_p, \ + float *: dbg_hexdump_float_p, \ + const float *: dbg_hexdump_const_float_p, \ + double *: dbg_hexdump_double_p, \ + const double *: dbg_hexdump_const_double_p, \ + default: dbg_hexdump_const_p) \ + (__FILE__, __LINE__, __func__, #expr, expr, size) + +/** + * For negative error codes. Returns the result of given expression. + */ +#define dbge(expr) dbg_error(__FILE__, __LINE__, __func__, #expr, expr) +#else +#define dbg(expr) (expr) +#define dbga(expr, length) (expr) +#define dbgb(expr) (expr) +#define dbgh(expr, size) (expr) +#define dbge(expr) (expr) +#endif + +/* Colorful output selection. */ +#ifndef DBG_NCOLOR +#define DBG_LOC "\x1b[02m" +#define DBG_EXPR "\x1b[0m\x1b[36m\x1b[1m" +#define DBG_VALUE "\x1b[01m" +#define DBG_RESET "\x1b[0m" +#define DBG_FORMAT(format) \ + DBG_LOC "%s:%d: (%s) " DBG_EXPR "%s" DBG_RESET " = " DBG_VALUE format \ + "\n" DBG_RESET +#define DBG_FORMAT_HEX(format, hexformat) \ + DBG_LOC "%s:%d: (%s) " DBG_EXPR "%s" DBG_RESET " = " DBG_VALUE format \ + " (0x" hexformat ")\n" DBG_RESET +#define DBG_FORMAT_ARRAY_BEGIN \ + DBG_LOC "%s:%d: (%s) " DBG_EXPR "%s" DBG_RESET " = " DBG_VALUE "[" +#define DBG_FORMAT_ARRAY_END "] (length: %u)\n" DBG_RESET +#define DBG_FORMAT_HEXDUMP_BEGIN \ + DBG_LOC "%s:%d: (%s) " DBG_EXPR "%s " DBG_RESET "(size: %u):\n" DBG_VALUE +#define DBG_FORMAT_HEXDUMP_END DBG_RESET +#define DBG_FORMAT_BACKTRACE \ + DBG_LOC "%s:%d: (%s) " DBG_RESET "Backtrace (most recent call last):\n" +#else +#define DBG_FORMAT(format) "%s:%d: (%s) %s = " format "\n" +#define DBG_FORMAT_HEX(format, hexformat) \ + "%s:%d: (%s) %s = " format " (0x" hexformat ")\n" +#define DBG_FORMAT_ARRAY_BEGIN "%s:%d: (%s) %s = [" +#define DBG_FORMAT_ARRAY_END "] (length: %u)\n" +#define DBG_FORMAT_HEXDUMP_BEGIN "%s:%d: (%s) %s (size: %u):\n" +#define DBG_FORMAT_BACKTRACE "%s:%d: (%s) Backtrace (most recent call last):\n" +#endif + +/* Custom output stream. */ +#ifdef DBG_OSTREAM +extern FILE *DBG_OSTREAM; +#else +#define DBG_OSTREAM stderr +#endif + +#define DBG_FUNC_CONST_P(name, type, format) \ + static inline const type *dbg_const_##name##_p( \ + const char *file_p, int line, const char *func_p, const char *expr_p, \ + const type *value_p, int length) { \ + int i; \ + char *delim_p; \ + \ + fprintf(DBG_OSTREAM, DBG_FORMAT_ARRAY_BEGIN, file_p, line, func_p, \ + expr_p); \ + delim_p = ""; \ + \ + for (i = 0; i < length; i++) { \ + fprintf(DBG_OSTREAM, "%s" format, delim_p, value_p[i]); \ + delim_p = ", "; \ + } \ + \ + fprintf(DBG_OSTREAM, DBG_FORMAT_ARRAY_END, length); \ + \ + return (value_p); \ + } + +#define DBG_FUNC_P(name, type, format) \ + static inline type *dbg_##name##_p(const char *file_p, int line, \ + const char *func_p, const char *expr_p, \ + type *value_p, int length) { \ + dbg_const_##name##_p(file_p, line, func_p, expr_p, value_p, length); \ + \ + return (value_p); \ + } + +#define DBG_FUNC_HEXDUMP_CONST_P(name, type) \ + static inline const type *dbg_hexdump_const_##name##_p( \ + const char *file_p, int line, const char *func_p, const char *expr_p, \ + const type *value_p, size_t size) { \ + dbg_hexdump(file_p, line, func_p, expr_p, (const void *)value_p, size); \ + \ + return (value_p); \ + } + +#define DBG_FUNC_HEXDUMP_P(name, type) \ + static inline type *dbg_hexdump_##name##_p( \ + const char *file_p, int line, const char *func_p, const char *expr_p, \ + type *value_p, size_t size) { \ + dbg_hexdump_const_##name##_p(file_p, line, func_p, expr_p, value_p, size); \ + \ + return (value_p); \ + } + +#define DBG_FUNC(name, type, format) \ + static inline type dbg_##name(const char *file_p, int line, \ + const char *func_p, const char *expr_p, \ + type value) { \ + fprintf(DBG_OSTREAM, DBG_FORMAT(format), file_p, line, func_p, expr_p, \ + value); \ + \ + return (value); \ + } \ + DBG_FUNC_CONST_P(name, type, format) \ + DBG_FUNC_P(name, type, format) \ + DBG_FUNC_HEXDUMP_CONST_P(name, type) \ + DBG_FUNC_HEXDUMP_P(name, type) + +#define DBG_FUNC_HEX(name, type, format, hexformat) \ + static inline type dbg_##name(const char *file_p, int line, \ + const char *func_p, const char *expr_p, \ + type value) { \ + fprintf(DBG_OSTREAM, DBG_FORMAT_HEX(format, hexformat), file_p, line, \ + func_p, expr_p, value, value); \ + \ + return (value); \ + } \ + DBG_FUNC_CONST_P(name, type, format) \ + DBG_FUNC_P(name, type, format) \ + DBG_FUNC_HEXDUMP_CONST_P(name, type) \ + DBG_FUNC_HEXDUMP_P(name, type) + +#define DBG_FUNC_CHAR_CONST_P(name, type) \ + static inline const type *dbg_const_##name##_p( \ + const char *file_p, int line, const char *func_p, const char *expr_p, \ + const type *value_p) { \ + fprintf(DBG_OSTREAM, DBG_FORMAT("\"%s\""), file_p, line, func_p, expr_p, \ + value_p); \ + \ + return (value_p); \ + } + +#define DBG_FUNC_CHAR_P(name, type) \ + static inline type *dbg_##name##_p(const char *file_p, int line, \ + const char *func_p, const char *expr_p, \ + type *value_p) { \ + dbg_const_##name##_p(file_p, line, func_p, expr_p, value_p); \ + \ + return (value_p); \ + } + +#define DBG_FUNC_CHAR(name, type, format) \ + static inline type dbg_##name(const char *file_p, int line, \ + const char *func_p, const char *expr_p, \ + type value) { \ + fprintf(DBG_OSTREAM, DBG_FORMAT(format), file_p, line, func_p, expr_p, \ + value); \ + \ + return (value); \ + } \ + DBG_FUNC_CHAR_CONST_P(name, type) \ + DBG_FUNC_CHAR_P(name, type) \ + DBG_FUNC_HEXDUMP_CONST_P(name, type) \ + DBG_FUNC_HEXDUMP_P(name, type) + +static inline const char *dbg_format_bool(bool value) { + return (value ? "true" : "false"); +} + +static inline bool dbg_bool(const char *file_p, int line, const char *func_p, + const char *expr_p, bool value) { + fprintf(DBG_OSTREAM, DBG_FORMAT("%s"), file_p, line, func_p, expr_p, + dbg_format_bool(value)); + + return (value); +} + +static inline const bool *dbg_const_bool_p(const char *file_p, int line, + const char *func_p, + const char *expr_p, + const bool *value_p, int length) { + int i; + char *delim_p; + + fprintf(DBG_OSTREAM, DBG_FORMAT_ARRAY_BEGIN, file_p, line, func_p, expr_p); + delim_p = ""; + + for (i = 0; i < length; i++) { + fprintf(DBG_OSTREAM, "%s%s", delim_p, dbg_format_bool(value_p[i])); + delim_p = ", "; + } + + fprintf(DBG_OSTREAM, DBG_FORMAT_ARRAY_END, length); + + return (value_p); +} + +static inline bool *dbg_bool_p(const char *file_p, int line, const char *func_p, + const char *expr_p, bool *value_p, int length) { + (void)dbg_const_bool_p(file_p, line, func_p, expr_p, value_p, length); + + return (value_p); +} + +static inline void *dbg_pointer(const char *file_p, int line, + const char *func_p, const char *expr_p, + void *value_p) { + fprintf(DBG_OSTREAM, DBG_FORMAT("%p"), file_p, line, func_p, expr_p, value_p); + + return (value_p); +} + +static inline void dbg_print_ascii(const uint8_t *buf_p, size_t size) { + size_t i; + + if (size < 8) { + fprintf(DBG_OSTREAM, " "); + } + + for (i = 0; i < 16 - size; i++) { + fprintf(DBG_OSTREAM, " "); + } + + fprintf(DBG_OSTREAM, "'"); + + for (i = 0; i < size; i++) { + fprintf(DBG_OSTREAM, "%c", isprint((int)buf_p[i]) ? buf_p[i] : '.'); + } + + fprintf(DBG_OSTREAM, "'"); +} + +static inline void dbg_hexdump(const char *file_p, int line, const char *func_p, + const char *expr_p, const void *buf_p, + size_t size) { + int pos; + const uint8_t *u8_buf_p; + + u8_buf_p = buf_p; + pos = 0; + + fprintf(DBG_OSTREAM, DBG_FORMAT_HEXDUMP_BEGIN, file_p, line, func_p, expr_p, + (int)size); + + while (size > 0) { + if ((pos % 16) == 0) { + fprintf(DBG_OSTREAM, " %08x: ", pos); + } + + fprintf(DBG_OSTREAM, "%02x ", u8_buf_p[pos] & 0xff); + + if ((pos % 16) == 7) { + fprintf(DBG_OSTREAM, " "); + } + + if ((pos % 16) == 15) { + dbg_print_ascii(&u8_buf_p[pos - 15], 16); + fprintf(DBG_OSTREAM, "\n"); + } + + pos++; + size--; + } + + if ((pos % 16) != 0) { + dbg_print_ascii(&u8_buf_p[pos - (pos % 16)], pos % 16); + fprintf(DBG_OSTREAM, "\n"); + } + +#ifdef DBG_FORMAT_HEXDUMP_END + fprintf(DBG_OSTREAM, DBG_FORMAT_HEXDUMP_END); +#endif +} + +static inline void *dbg_hexdump_p(const char *file_p, int line, + const char *func_p, const char *expr_p, + void *buf_p, size_t size) { + dbg_hexdump(file_p, line, func_p, expr_p, buf_p, size); + + return (buf_p); +} + +static inline const void *dbg_hexdump_const_p(const char *file_p, int line, + const char *func_p, + const char *expr_p, + const void *buf_p, size_t size) { + dbg_hexdump(file_p, line, func_p, expr_p, buf_p, size); + + return (buf_p); +} + +DBG_FUNC_CHAR(char, char, "%hhi") +DBG_FUNC_CHAR(schar, signed char, "%hhi") +DBG_FUNC_CHAR(uchar, unsigned char, "%hhu") +DBG_FUNC_HEX(short, short, "%hi", "%hx") +DBG_FUNC_HEX(ushort, unsigned short, "%hu", "%hx") +DBG_FUNC_HEX(int, int, "%d", "%x") +DBG_FUNC_HEX(uint, unsigned int, "%u", "%x") +DBG_FUNC_HEX(long, long, "%ld", "%lx") +DBG_FUNC_HEX(ulong, unsigned long, "%lu", "%lx") +DBG_FUNC_HEX(llong, long long, "%lld", "%llx") +DBG_FUNC_HEX(ullong, unsigned long long, "%llu", "%llx") +DBG_FUNC(float, float, "%f") +DBG_FUNC(double, double, "%lf") + +static inline int dbg_error(const char *file_p, int line, const char *func_p, + const char *expr_p, int error) { + char string[128]; + char buf[256]; + + if (error < 0) { + strerror_r(-error, &string[0], sizeof(string)); + snprintf(&buf[0], sizeof(buf), "%d (%s)", error, &string[0]); + buf[sizeof(buf) - 1] = '\0'; + fprintf(DBG_OSTREAM, DBG_FORMAT("%s"), file_p, line, func_p, expr_p, + &buf[0]); + } else { + dbg_int(file_p, line, func_p, expr_p, error); + } + + return (error); +} + +#ifdef __GLIBC__ + +#define DBG_BACKTRACE_MAX 100 +#define DBG_TOKENPASTE(x, y) DBG_TOKENPASTE2(x, y) +#define DBG_TOKENPASTE2(x, y) x##y +#define DBG_UNIQUE(x) DBG_TOKENPASTE(x, DBG_TOKENPASTE(___, __LINE__)) + +/** + * Print a backtrace. Pass -rdynamic to the linker for function names. + */ +#define dbgbt() \ + do { \ + void *DBG_UNIQUE(buf)[DBG_BACKTRACE_MAX]; \ + \ + dbg_format_backtrace(__FILE__, __LINE__, __func__, &DBG_UNIQUE(buf)[0], \ + backtrace(&DBG_UNIQUE(buf)[0], DBG_BACKTRACE_MAX)); \ + } while (0) + +static inline int dbg_format_backtrace_addr2line(void **addresses_pp, + int depth) { +#ifdef DBG_ADDR2LINE + + char exe[256]; + char command[384]; + ssize_t size; + int res; + int i; + + size = readlink("/proc/self/exe", &exe[0], sizeof(exe) - 1); + + if (size == -1) { + printf("No executable found!\n"); + + return (-1); + } + + exe[size] = '\0'; + + for (i = (depth - 1); i >= 0; i--) { + snprintf(&command[0], sizeof(command), "addr2line -f -p -e %s %p", &exe[0], + ((void *)(((uintptr_t)(addresses_pp[i])) - 1))); + command[sizeof(command) - 1] = '\0'; + + res = system(&command[0]); + + if (res == -1) { + return (-1); + } else if (WIFEXITED(res)) { + if (WEXITSTATUS(res) != 0) { + return (-1); + } + } else { + return (-1); + } + } + + return (0); + +#else + + (void)addresses_pp; + (void)depth; + + return (-1); + +#endif +} + +static inline void dbg_format_backtrace_symbols(void **addresses_pp, + int depth) { + char **strings_pp; + int i; + + strings_pp = backtrace_symbols(addresses_pp, depth); + + if (strings_pp == NULL) { + printf(" No strings found!\n"); + } else { + for (i = (depth - 1); i >= 0; i--) { + printf(" %s\n", strings_pp[i]); + } + + free(strings_pp); + } +} + +static inline void dbg_format_backtrace(const char *file_p, int line, + const char *func_p, void **addresses_pp, + int depth) { + printf(DBG_FORMAT_BACKTRACE, file_p, line, func_p); + + if (dbg_format_backtrace_addr2line(addresses_pp, depth) != 0) { + dbg_format_backtrace_symbols(addresses_pp, depth); + } +} + +#endif + +#endif diff --git a/log.c b/log.c new file mode 100644 index 0000000..bf75dab --- /dev/null +++ b/log.c @@ -0,0 +1,75 @@ +/* + * This file is originally part of the SLOWProxy + * (https://github.com/nicholascw/SLOWProxy). + * + * Copyright (C) 2020-2021 Nicholas Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "log.h" + +static loglevel_t setted_lvl = ERR; +static const char *colors[] = {"\x1b[0;32m", "\x1b[1;36m", "\x1b[1;30m", + "\x1b[1;33m", "\x1b[1;31m", "\x1b[0m"}; + +void slow_print_log(loglevel_t lvl, const char *func_name, + const char *err_string, const char *src_name, + const unsigned int lineno) { + if (lvl < setted_lvl || setted_lvl == 5) return; + char str_lvl[25]; + switch (lvl) { + case DBG: + snprintf(str_lvl, sizeof(str_lvl), "%s[Debug]%s", colors[DBG], colors[5]); + break; + case INFO: + snprintf(str_lvl, sizeof(str_lvl), "%s[Info]%s", colors[INFO], colors[5]); + break; + case WARN: + snprintf(str_lvl, sizeof(str_lvl), "%s[Warn]%s", colors[WARN], colors[5]); + break; + case ERR: + snprintf(str_lvl, sizeof(str_lvl), "%s[Error]%s", colors[ERR], colors[5]); + break; + default: + snprintf(str_lvl, sizeof(str_lvl), "%s[Panic]%s", colors[ERR], colors[5]); + } + + time_t t = time(NULL); + struct tm curr_tm; + if (!localtime_r(&t, &curr_tm)) perror("localtime_r()"); + char str_time[30]; + str_time[29] = '\0'; + if (!strftime((char *)&str_time, sizeof(str_time), "%FT%T%z", &curr_tm)) + fprintf(stderr, "strftime() failed.\n"); + //[Debug] 2020-07-08 03:27:36 CDT + (setted_lvl != DBG) + ? fprintf(stderr, "%s\t%s%s%s: %s(): %s\n", str_lvl, colors[0], str_time, + colors[5], func_name, err_string) + : fprintf(stderr, "%s\t%s%s%s: %s:%d: %s(): %s\n", str_lvl, colors[0], + str_time, colors[5], src_name, lineno, func_name, err_string); +} + +void slow_perror_log(const char *func_name, const char *src_name, + const unsigned int lineno) { + /*char str_errno[128]; + str_errno[127] = '\0'; + if(strerror_r(errno, str_errno, 127)!=0) perror("slow_perror_log()"); + else slow_print_log(ERR, func_name, str_errno, src_name, lineno);*/ + slow_print_log(ERR, func_name, strerror(errno), src_name, lineno); +} + +loglevel_t slow_loglevel(loglevel_t level) { + if (level != INVALID) setted_lvl = level; + return setted_lvl; +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..fff3e20 --- /dev/null +++ b/log.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include +#include + +#include "dbg.h" // https://github.com/eerimoq/dbg-macro + +static char slow_snprintf_buf[1024]; + +#define __FNAME__ \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#define L_DEBUG(STR) \ + (slow_print_log(DBG, __FUNCTION__, (STR), (__FNAME__), __LINE__)); +#define L_INFO(STR) \ + (slow_print_log(INFO, __FUNCTION__, (STR), (__FNAME__), __LINE__)); +#define L_WARN(STR) \ + (slow_print_log(WARN, __FUNCTION__, (STR), (__FNAME__), __LINE__)); +#define L_ERR(STR) \ + (slow_print_log(ERR, __FUNCTION__, (STR), (__FNAME__), __LINE__)); +#define L_PERROR() (slow_perror_log(__FUNCTION__, (__FNAME__), __LINE__)); + +#define L_DEBUGF(FMT, ...) \ + do { \ + snprintf(slow_snprintf_buf, 1024, (FMT), __VA_ARGS__); \ + L_DEBUG(slow_snprintf_buf); \ + } while (0); +#define L_INFOF(FMT, ...) \ + do { \ + snprintf(slow_snprintf_buf, 1024, (FMT), __VA_ARGS__); \ + L_INFO(slow_snprintf_buf); \ + } while (0); +#define L_WARNF(FMT, ...) \ + do { \ + snprintf(slow_snprintf_buf, 1024, (FMT), __VA_ARGS__); \ + L_WARN(slow_snprintf_buf); \ + } while (0); +#define L_ERRF(FMT, ...) \ + do { \ + snprintf(slow_snprintf_buf, 1024, (FMT), __VA_ARGS__); \ + L_ERR(slow_snprintf_buf); \ + } while (0); + +typedef enum { + DBG = 1, + INFO = 2, + WARN = 3, + ERR = 4, + SHUT = 5, + INVALID = -1 +} loglevel_t; + +void slow_print_log(loglevel_t lvl, const char *func_name, + const char *err_string, const char *src_name, + const unsigned int lineno); + +void slow_perror_log(const char *func_name, const char *src_name, + const unsigned int lineno); + +loglevel_t slow_loglevel(loglevel_t level); diff --git a/main.c b/main.c index ef09445..742d2fe 100644 --- a/main.c +++ b/main.c @@ -1,9 +1,92 @@ #include -int surf_internet() { - return 0; -} +#include "log.h" +#include "parser.h" +#include "stream.h" + +int main(int argc, char *argv[]) { + s_uri uri_parsed; + memset(&uri_parsed, 0, sizeof(s_uri)); + if (argc < 2) { + L_ERRF("Usage: %s [OPTIONS] URL\n", argv[0]); + return 0; + } + if (argc > 2) { + // where we'll parse options + for (int i = 1; i < argc - 1; i++) { + if (strstr(argv[i], "-v") == argv[i]) slow_loglevel(DBG); + } + } + if (parse_uri(argv[argc - 1], &uri_parsed)) { + L_ERRF("Fatal error when parsing URL: %s", argv[argc - 1]); + return 1; + } + + struct addrinfo hints, *servinfo, *p; + int rv; + char s[INET6_ADDRSTRLEN]; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((rv = getaddrinfo(uri_parsed.hostname, uri_parsed.port, &hints, + &servinfo)) != 0) { + L_ERRF("getaddrinfo: %s", gai_strerror(rv)); + return 1; + } + int sockfd; + // loop through all the results and connect to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + L_PERROR(); + continue; + } -int main() { - return surf_internet(); + if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + L_PERROR(); + continue; + } + + break; + } + + if (p == NULL) { + L_ERR("failed to connect.\n"); + return 2; + } + + inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, + sizeof s); + L_DEBUGF("connecting to %s...", s); + + freeaddrinfo(servinfo); // all done with this structure + + char *buf = calloc(MAXDATASIZE, 1); + if (!buf) { + L_PERROR(); + exit(1); + } + snprintf(buf, MAXDATASIZE, + "GET %s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Connection: close\r\n" + "Accept: */*\r\n" + "User-Agent: monyhar-greasy/0.1 (like curl/7.72.0)\r\n\r\n", + uri_parsed.path, uri_parsed.hostname, uri_parsed.port); + if (sendall(sockfd, buf, strlen(buf)) < 0) { + L_ERR("Error occured when sending HTTP Request."); + exit(1); + } + free(buf); + buf = NULL; + ssize_t content_size = recv_to_fd(sockfd, STDOUT_FILENO); + if (content_size > 0) { + L_INFOF("Successfully received %ld byte(s) of payload. Bye.", content_size); + } else if (content_size < 0) { + L_ERR("Error when handling HTTP Response."); + } + close(sockfd); + return 0; } diff --git a/parser.c b/parser.c new file mode 100644 index 0000000..79bfbe7 --- /dev/null +++ b/parser.c @@ -0,0 +1,50 @@ +#include "parser.h" + +#include +#include +#define larger(X, Y) (X > Y ? X : Y) +#define smaller(X, Y) (X < Y ? X : Y) + +int parse_uri(char *str, s_uri *res) { + char *pos = strstr(str, "://"); + // assumes http by if protocol header not found + res->proto = + pos ? strndup(str, smaller((pos - str), PROTO_MAXLEN)) : strdup("http"); + pos = pos ? pos + 3 : str; + for (int i = strlen(res->proto) - 1; i >= 0; i--) { + res->proto[i] = tolower(res->proto[i]); + } + int host_len; + if ((host_len = strchr(pos, ':') - pos) < HOSTNAME_MAXLEN && host_len > 0) { + res->hostname = strndup(pos, smaller(host_len, HOSTNAME_MAXLEN)); + pos += host_len + 1; + char *port_ends_at; + if ((port_ends_at = strchr(pos, '/'))) { + if (port_ends_at - pos < 10) { + res->port = strndup(pos, port_ends_at - pos); + } else { + L_ERR("Invalid port: too long."); + } + } else { + if (strlen(pos) < 10) { + res->port = strndup(pos, 9); + } else { + L_ERR("Invalid URL"); + } + } + } else { + res->port = strchr(res->proto, 's') ? strdup("443") : strdup("80"); + if ((host_len = strchr(pos, '/') - pos) < HOSTNAME_MAXLEN && host_len > 0) { + res->hostname = strndup(pos, host_len); + } else if ((host_len = strlen(pos)) < HOSTNAME_MAXLEN) { + res->hostname = strndup(pos, host_len); + } else { + L_ERR("Invalid URL"); + return 1; + } + } + + res->path = + strchr(pos, '/') ? strndup(strchr(pos, '/'), URL_MAXLEN) : strdup("/"); + return 0; +} diff --git a/parser.h b/parser.h new file mode 100644 index 0000000..715774a --- /dev/null +++ b/parser.h @@ -0,0 +1,13 @@ +#include "log.h" +#define PROTO_MAXLEN 10 +#define URL_MAXLEN 6144 +#define HOSTNAME_MAXLEN 255 + +typedef struct { + char *proto; + char *hostname; + char *port; + char *path; +} s_uri; + +int parse_uri(char *str, s_uri *res); diff --git a/stream.c b/stream.c new file mode 100644 index 0000000..b6b9e0d --- /dev/null +++ b/stream.c @@ -0,0 +1,174 @@ +#include "stream.h" + +#include "log.h" + +void *get_in_addr(struct sockaddr *sa) { + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in *)sa)->sin_addr); + } + + return &(((struct sockaddr_in6 *)sa)->sin6_addr); +} + +ssize_t sendall(int sockfd, void *buffer, size_t length) { + size_t sent = 0; + ssize_t ret; + do { + ret = send(sockfd, buffer + sent, length - sent, 0); + if (ret > 0) { + sent += ret; + } else { + if (errno == EINTR) + continue; + else { + L_PERROR(); + return -1; + } + } + } while (sent < length); + return sent; +} + +ssize_t recvall(int sockfd, void *buffer, size_t length) { + size_t recved = 0; + ssize_t ret; + do { + ret = recv(sockfd, buffer + recved, length - recved, 0); + if (ret > 0) { + recved += ret; + } else if (ret == 0) { + break; + } else { + if (errno == EINTR) { + ret = 0; + continue; + } else { + L_PERROR(); + return -1; + } + } + } while (recved < length); + return recved; +} + +ssize_t recv_line(int sockfd, char *buf, size_t bufsize) { + char *ptr = buf; + ssize_t ret = 0; + do { + ptr += ret; + if (ptr - buf > (ssize_t)bufsize) return -1; + ret = recv(sockfd, ptr, 1, 0); + if (ret < 0) { + if (errno == EINTR) { + ret = 0; + continue; + } else { + L_PERROR(); + return -1; + } + } + } while (!strstr(buf, "\r\n")); + return (ptr - buf) + ret; +} + +ssize_t recv_to_fd(int sockfd, int filefd) { + ssize_t recv_size = 0, claimed_size = 0; + char *buf = calloc(MAXDATASIZE, 1); + if (!buf) { + L_PERROR(); + return -1; + } + char *ptr = buf; + + // read http status line + ssize_t ret = recv_line(sockfd, buf, MAXDATASIZE); + if (ret < 0) { + free(buf); + return -1; + } + char *spliter; + if ((spliter = strchr(buf, ' '))) { + if (strcasestr(spliter, "200 OK")) { + L_INFO("Server responsed: 200 OK..."); + ptr += ret; + } else { + char tmp[20]; + memset(tmp, 0, 20); + strncpy(tmp, spliter + 1, strchr(spliter, '\r') - spliter - 1); + L_INFOF("Server responsed: %s", tmp); + free(buf); + return 0; + } + ret = 0; + while (!strstr(ptr - 2, "\r\n\r\n")) { + ptr += ret; + ret = recv_line(sockfd, ptr, MAXDATASIZE - (buf - ptr)); + if (ret < 0) { + free(buf); + return -1; + } + if (strcasestr(ptr, "Content-Length:")) { + claimed_size = strtol(ptr + strlen("Content-Length:"), NULL, 10); + L_INFOF("Header claimed content size of %ld bytes.", claimed_size); + } + } + ptr += ret; + L_DEBUGF("Header Length: %ld bytes", (ptr - buf)); + + if (filefd < 0) { + L_PERROR(); + free(buf); + return -1; + } + L_DEBUGF("output opened at fd=%d", filefd); + memset(buf, 0, (ptr - buf) + 1); + while ((ret = recv(sockfd, buf, MAXDATASIZE, 0))) { + if (ret < 0) { + if (errno == EINTR) { + ret = 0; + continue; + } else { + L_PERROR(); + close(filefd); + free(buf); + return -1; + } + } + recv_size += ret; + ssize_t wr_ret = 0, written_size = 0; + while (written_size < ret) { + wr_ret = write(filefd, buf, ret - written_size); + if (wr_ret < 0) { + if (errno == EINTR) { + wr_ret = 0; + continue; + } else { + L_PERROR(); + close(filefd); + free(buf); + return -1; + } + } + written_size += wr_ret; + L_DEBUGF("Received %ld bytes and written to output", wr_ret); + L_INFOF("Received %ld bytes in total", recv_size); + } + } + if (fdatasync(filefd)) { + L_DEBUG("I/O operations may not be sync'd yet."); + } + + close(filefd); + + if (claimed_size != recv_size) { + L_WARNF("claimed %ld byte(s), written %ld byte(s)", claimed_size, + recv_size); + } + } else { + L_ERR("Invalid HTTP Response.\n"); + free(buf); + return -1; + } + free(buf); + return recv_size; +} diff --git a/stream.h b/stream.h new file mode 100644 index 0000000..0561018 --- /dev/null +++ b/stream.h @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include + +#define MAXDATASIZE 16384 + +void *get_in_addr(struct sockaddr *sa); +ssize_t sendall(int sockfd, void *buffer, size_t length); +ssize_t recvall(int sockfd, void *buffer, size_t length); +ssize_t recv_line(int sockfd, char *buf, size_t bufsize); +ssize_t recv_to_fd(int sockfd, int filefd); From 14a09858d4a5a899e72d146651f5b3947150891c Mon Sep 17 00:00:00 2001 From: Nicholas Wang <7414442+nicholascw@users.noreply.github.com> Date: Tue, 29 Jun 2021 14:24:23 -0500 Subject: [PATCH 3/3] exit on unknown protocols --- main.c | 9 ++++++++- stream.c | 7 ++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/main.c b/main.c index 742d2fe..d8fe5af 100644 --- a/main.c +++ b/main.c @@ -17,11 +17,17 @@ int main(int argc, char *argv[]) { if (strstr(argv[i], "-v") == argv[i]) slow_loglevel(DBG); } } + if (parse_uri(argv[argc - 1], &uri_parsed)) { L_ERRF("Fatal error when parsing URL: %s", argv[argc - 1]); return 1; } + if (strcmp(uri_parsed.proto, "http")) { + L_ERRF("Unsupported protocol: %s", uri_parsed.proto); + exit(1); + } + struct addrinfo hints, *servinfo, *p; int rv; char s[INET6_ADDRSTRLEN]; @@ -59,7 +65,8 @@ int main(int argc, char *argv[]) { inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s); - L_DEBUGF("connecting to %s...", s); + L_DEBUGF("connecting to %s (%s:%s)...", uri_parsed.hostname, s, + uri_parsed.port); freeaddrinfo(servinfo); // all done with this structure diff --git a/stream.c b/stream.c index b6b9e0d..52b2b90 100644 --- a/stream.c +++ b/stream.c @@ -92,9 +92,10 @@ ssize_t recv_to_fd(int sockfd, int filefd) { L_INFO("Server responsed: 200 OK..."); ptr += ret; } else { - char tmp[20]; - memset(tmp, 0, 20); - strncpy(tmp, spliter + 1, strchr(spliter, '\r') - spliter - 1); + char tmp[100]; + memset(tmp, 0, 100); + int r_code_len = strchr(spliter, '\r') - spliter - 1; + strncpy(tmp, spliter + 1, r_code_len > 99 ? 99 : r_code_len); L_INFOF("Server responsed: %s", tmp); free(buf); return 0;