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/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%。有史前上网功能。 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..d8fe5af 100644 --- a/main.c +++ b/main.c @@ -1,9 +1,99 @@ #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; + } + + 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]; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; -int main() { - return surf_internet(); + 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; + } + + 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:%s)...", uri_parsed.hostname, s, + uri_parsed.port); + + 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..52b2b90 --- /dev/null +++ b/stream.c @@ -0,0 +1,175 @@ +#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[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; + } + 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);