diff --git a/readme.md b/readme.md index 7bd1492..b2e15fc 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@

Morning Tavern

放置一些终端小工具与代码包

-[![Version](https://img.shields.io/badge/Version-1.0.1-207F4C)](https://github.com/waitspring/morning-tavern) +[![Version](https://img.shields.io/badge/Version-1.0.2-207F4C)](https://github.com/waitspring/morning-tavern) [![License](https://img.shields.io/badge/License-Apache%202.0-373834)](https://github.com/waitspring/morning-tavern/blob/master/license) diff --git a/static/morning-tavern/tcping/configure.ac b/static/morning-tavern/tcping/configure.ac index 7149b8e..a4466a9 100644 --- a/static/morning-tavern/tcping/configure.ac +++ b/static/morning-tavern/tcping/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.69]) -AC_INIT([morning-tavern], [1.0.1], [newsfuxuanming@foxmail.com]) +AC_INIT([morning-tavern], [1.0.2], [newsfuxuanming@foxmail.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([src/tcping.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/static/morning-tavern/tcping6/Makefile.am b/static/morning-tavern/tcping6/Makefile.am new file mode 100644 index 0000000..21c85db --- /dev/null +++ b/static/morning-tavern/tcping6/Makefile.am @@ -0,0 +1,4 @@ +AUTOMAKE_OPTIONS = foreign +bin_PROGRAMS = tcping6 +tcping6_SOURCES = src/tcping6.c + diff --git a/static/morning-tavern/tcping6/configure.ac b/static/morning-tavern/tcping6/configure.ac new file mode 100644 index 0000000..316dcee --- /dev/null +++ b/static/morning-tavern/tcping6/configure.ac @@ -0,0 +1,16 @@ +AC_PREREQ([2.69]) +AC_INIT([morning-tavern], [1.0.2], [newsfuxuanming@foxmail.com]) +AM_INIT_AUTOMAKE +AC_CONFIG_SRCDIR([src/tcping6.c]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile]) + +AC_PROG_CC + +AC_CHECK_HEADERS([arpa/inet.h netdb.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) + +AC_FUNC_ERROR_AT_LINE +AC_CHECK_FUNCS([gettimeofday memset socket strrchr]) + +AC_OUTPUT + diff --git a/static/spec/morning-tavern.spec b/static/spec/morning-tavern.spec index 021a28e..7ed29cb 100755 --- a/static/spec/morning-tavern.spec +++ b/static/spec/morning-tavern.spec @@ -1,5 +1,5 @@ Name: morning-tavern -Version: 1.0.1 +Version: 1.0.2 Release: 1%{?dist} Summary: A good repository for bash tools @@ -23,11 +23,15 @@ A good repository for bash tools cd %{_builddir}/%{name}-%{version}/tcping %configure make CFLAGS=-std=c99 %{?_smp_mflags} +cd %{_builddir}/%{name}-%{version}/tcping6 +%configure +make CFLAGS=-std=c99 %{?_smp_mflags} %install mkdir -p %{buildroot}%{_bindir} install -m 0775 %{_builddir}/%{name}-%{version}/searchme/searchme %{buildroot}%{_bindir}/searchme install -m 0775 %{_builddir}/%{name}-%{version}/tcping/tcping %{buildroot}%{_bindir}/tcping +install -m 0775 %{_builddir}/%{name}-%{version}/tcping6/tcping6 %{buildroot}%{_bindir}/tcping6 mkdir -p %{buildroot}%{_sysconfdir} install -m 0664 %{_builddir}/%{name}-%{version}/searchme/searchme.conf %{buildroot}%{_sysconfdir}/searchme.conf @@ -35,8 +39,13 @@ install -m 0664 %{_builddir}/%{name}-%{version}/searchme/searchme.conf %{buildro %{_bindir}/searchme %{_sysconfdir}/searchme.conf %{_bindir}/tcping +%{_bindir}/tcping6 %changelog +* Fri Jan 07 2023 waitspring +- Create the package for tcping6 command +- Create the package for tcping command +- Create the package for searchme command * Fri Dec 30 2022 waitspring - Create the package for tcping command - Create the package for searchme command diff --git a/static/spec/readme.md b/static/spec/readme.md index f7a319e..ee4ad40 100644 --- a/static/spec/readme.md +++ b/static/spec/readme.md @@ -1,14 +1,24 @@ # RPM 仓库结构 -`/srv/morning-tavern-1.0.1` 的目录结构如下: +`/srv/morning-tavern-1.0.2` 的目录结构如下: ``` -/srv/morning-tavern-1.0.1 +/srv/morning-tavern-1.0.2 │ ├──── searchme │ ├──── searchme │ └──── searchme.conf - └──── tcping + ├──── tcping + │ ├──── aclocal.m4 + │ ├──── config.h.in + │ ├──── configure + │ ├──── configure.ac + │ ├──── Makefile.am + │ ├──── Makefile.in + │ └──── src + │ ├──── tcping + │ └──── tcping.c + └──── tcping6 ├──── aclocal.m4 ├──── config.h.in ├──── configure @@ -16,8 +26,8 @@ ├──── Makefile.am ├──── Makefile.in └──── src - ├──── tcping - └──── tcping.c + ├──── tcping6 + └──── tcping6.c ``` diff --git a/tcping/tcping.c b/tcping/tcping.c index 71c7aeb..43f2567 100644 --- a/tcping/tcping.c +++ b/tcping/tcping.c @@ -238,7 +238,7 @@ int main(int argc, char *argv[]) { char *parameter; char ip[256]; char po[256]; - char *ipp; // 指向清洗过的 IPv4/IPv6 地址 + char *ipp; // 指向清洗过的 IPv4 地址 char *pop; // 指向清洗过的端口号 struct sockaddr_in addr; diff --git a/tcping6/readme.md b/tcping6/readme.md new file mode 100644 index 0000000..5b39a24 --- /dev/null +++ b/tcping6/readme.md @@ -0,0 +1,37 @@ +# Tcping6 + +### 一、使用说明 + +**Tcping6** 小工具在 Github 开源社区有很多作者实现过, 但从使用体验的层面看始终存在各种小瑕疵 + +**Tcping6** 小工具由本人重构之后, 具备下述特点: + ++ 允许在 `Shell` 脚本内配合 `IFS` 命令与循环结构使用 ++ 允许使用静默工作模式 ++ 根据执行结果的不同有不同的退出码: + * TCP 连接全部建立成功使用数值 0 作为退出码 + * TCP 连接部分建立成功使用数值 1 作为退出码 + * TCP 连接全部建立失败使用数值 2 作为退出码 + +### 二、使用方法 + +**Tcping6** 小工具的安装方法说明如下: + +```bash +$ gcc --std=c99 -o /usr/bin/tcping6 /path/to/tcping6.c +$ chmod a+x /usr/bin/tcping6 +``` + +**Tcping6** 小工具的使用方法说明如下: + +```bash +$ tcping6 [option...] socket + + -c, --count=TIMES # 配置命令发起 TIMES 次 TCP 连接, 默认值为 86400 (保持长 TCPING) + -i, --interval=NUM # 配置两次连接中的停顿时间为 NUM 秒钟, 默认值为 1 秒钟 + -q, --quiet # 开启静默执行模式, 命令屏蔽正常输出 + -h, --help # 列出命令的帮助信息 + -v, --version # 列出命令的版本信息 + +``` + diff --git a/tcping6/tcping6.c b/tcping6/tcping6.c new file mode 100644 index 0000000..8ed6f77 --- /dev/null +++ b/tcping6/tcping6.c @@ -0,0 +1,337 @@ +/********************************************************************************************************************** + * * + * ______ __ * + * (_) | o / * + * | __ _ _ _ __,| __ * + * _ |/ |/ \_| / |/ | / ||/ \ * + * (_/ \___/|__/ |_/ | |_/\_/|/\__/ * + * /| /| * + * \| \| * + * * + ********************************************************************************************************************** + * 文件名称: tcping6.c + * 编译方法: gcc --std=c99 -o /usr/bin/tcping6 /path/to/tcping6.c + * 作者信息: Fu Xuanming (2023) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/********************************************************************************************************************** + * Function * + ********************************************************************************************************************** + */ +void usage(void) { + puts("Usage: tcping6 [option...] socket"); + puts(""); + puts(" -c, --count=NUM make NUM times TCP connection, default 86400"); + puts(" -i, --interval=NUM pause time for two connections as NUM seconds, default 1"); + puts(" -q, --quiet enable the silent execution mode"); + puts(" -h, --help list this help information"); + puts(" -v, --version list this command version information"); + puts(""); +} + +void version(void) { + puts(""); + puts("command version: alpha 1.0.0"); + puts("created by FuXuanming (waitspring)"); + puts(""); +} + +void info(const char *func, const char *info) { + char timestamp[64]; + time_t timepoint = time(NULL); + struct tm *timeinfo = localtime(&timepoint); + struct timeval tv; // 使用 sys/time.h/timeval 数据结构获取毫秒级时间戳 + + gettimeofday(&tv, NULL); + strftime(timestamp, sizeof timestamp, "%F %T", timeinfo); + fprintf(stdout, "%s.%03d INFO [%s] %s\n", timestamp, tv.tv_usec/1000, func, info); +} + +void warn(const char *func, const char *warn) { + char timestamp[64]; + time_t timepoint = time(NULL); + struct tm *timewarn = localtime(&timepoint); + struct timeval tv; + + gettimeofday(&tv, NULL); + strftime(timestamp, sizeof timestamp, "%F %T", timewarn); + fprintf(stdout, "%s.%03d WARN [%s] %s\n", timestamp, tv.tv_usec/1000, func, warn); +} + +void error(const char *func, const char *error) { + char timestamp[64]; + time_t timepoint = time(NULL); + struct tm *timerror = localtime(&timepoint); + struct timeval tv; + + gettimeofday(&tv, NULL); + strftime(timestamp, sizeof timestamp, "%F %T", timerror); + fprintf(stderr, "%s.%03d ERROR [%s] %s\n", timestamp, tv.tv_usec/1000, func, error); +} + + + +/********************************************************************************************************************** + * Function * + ********************************************************************************************************************** + */ +int judge_ip(char *ip, char **ipp, struct sockaddr_in6 *addr6) { + /* Todo: 分切套接字得到 IPv6 地址, 验证 IPv6 地址是否符合书写规范 + * Todo: 已经验证通过的 IPv6 地址, 赋予 sockaddr_in6.sin6_addr.s6_addr 数据字段 + * Todo: 已经验证通过的 IPv6 地址, 赋予 ipp 字符串指针 + * Todo: 如果传入参数为 IPv4 地址, 抛出错误信息 + * Return: IPv6 地址校验成功返回值为 1, IPv6 地址校验失败返回值为 0 + */ + struct in_addr ipv4; + struct in6_addr ipv6; + const char tag = ':'; + const char sign[2] = {'[', ']'}; + + ip[strlen(ip) - strlen(strrchr(ip, tag))] = '\0'; + if (strrchr(ip, sign[0])) { + ip = &strrchr(ip, sign[0])[1]; + } + if (strrchr(ip, sign[1])) { + ip[strlen(ip) - strlen(strrchr(ip, sign[1]))] = '\0'; + } + if (inet_pton(AF_INET, (char *)ip, &ipv4)) { + error("judge_ip", "check target address error"); + error("judge_ip", "target address is IPv4 address, maybe you can use tcping command"); + return 0; + } + if (inet_pton(AF_INET6, (char *)ip, &ipv6)) { + addr6->sin6_family = AF_INET6; + inet_pton(AF_INET6, (char *)ip, &(addr6->sin6_addr)); + *ipp = ip; + return 1; + } + + error("judge_ip", "check target address error"); + error("judge_ip", "target address does not conform to the IPv6 writing specification"); + return 0; +} + +int judge_po(char *po, char **pop, struct sockaddr_in6 *addr6) { + /* Todo: 分切套接字得到端口号, 验证端口号是否符合书写规范 + * Todo: 已经验证通过的端口号, 赋予 sockaddr_in6.sin6_port 数据字段 + * Todo: 已经验证通过的端口号, 赋予 pop 字符串指针 + * Return: 端口号校验成功返回值为 1, 端口号校验失败返回值为 0 + */ + const char tag = ':'; + + po = &strrchr(po, tag)[1]; + for (int i = 0; i < strlen(po); i++) { + if (po[i] >= '0' && po[i] <='9') { + continue; + } else { + error("judge_po", "check target port error"); + error("judge_po", "target port does not conform to the port writing specification"); + return 0; + } + } + if (atoi(po) > 65535) { + error("judge_po", "check target port error"); + error("judge_po", "target port has exceeded the value range"); + return 0; + } + + addr6->sin6_port = htons(atoi(po)); + *pop = po; + return 1; +} + +int connect_to(struct timeval *begin, struct timeval *end, struct sockaddr_in6 *addr6) { + /* Todo: 创建套接字, 并发起 TCP 连接 (默认 10 秒钟超时) + * Todo: 针对上述过程计时, 返回计时结果, 若连接失败则计时结果为 0 + * Notice: 套接字的非阻塞工作模式无法适应内网的 TCP 连接 + */ + int sock; + int connect_result; + int ms; + int timeout = 10; + + gettimeofday(begin, NULL); + sock = socket(addr6->sin6_family, SOCK_STREAM, 0); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int)); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int)); + connect_result = connect(sock, (struct sockaddr *)addr6, sizeof(*addr6)); + close(sock); + gettimeofday(end, NULL); + if (connect_result == 0) { + ms = (1000000 * (end->tv_sec - begin->tv_sec) + (end->tv_usec - begin->tv_usec)) / 1000; + if (ms == 0) { + /* Todo: 建立本机的 TCP 连接时, 因数据精度不够, 可能导致计时为 0 毫秒, 人工将其重置为 1 毫秒 + */ + ms = 1; + } + return ms; + } else { + return 0; + } +} + +int max(int *num, int size) { + int max = num[0]; + + for (int i = 0; i < size; i++) { + if (max < num[i] && num[i] != 0) { + max = num[i]; + } + } + + return max; +} + +int min(int *num, int size) { + int min = num[0]; + + for (int i = 0; i < size; i++) { + if (min > num[i] && num[i] != 0) { + min = num[i]; + } + } + + return min; +} + +int avg(int *num, int size, int counts) { + int total = 0; + + for (int i = 0; i < size; i++) { + if (num[i] != 0) { + total += num[i]; + } + } + + if (counts != 0) { + return total / counts; + } else { + return 0; + } +} + + + +/********************************************************************************************************************** + * Main Part * + ********************************************************************************************************************** + */ +int main(int argc, char *argv[]) { + int opt; + int count = 86400; // count = 86400 设置连接次数, 默认为 86400 次连接 (24小时不间断) + int interval = 1; // interval = 1 设置两次连接中的停顿时间为 1 秒钟 + int quiet = 0; // quiet = 0 关闭静默执行模式, quiet = 1 开启静默执行模式 + + char *parameter; + char ip[256]; + char po[256]; + char *ipp; // 指向清洗过的 IPv6 地址 + char *pop; // 指向清洗过的端口号 + + struct sockaddr_in6 addr6; + memset(&addr6, 0, sizeof(addr6)); // 使用数值 0 填充数据结构 sockaddr_in6 的每个字节 + + while ((opt = getopt(argc, argv, "c:i:qhv")) != -1) { + switch (opt) { + case 'c': + count = atoi(optarg); + break; + case 'i': + interval = atoi(optarg); + break; + case 'q': + quiet = 1; + break; + case 'h': + usage(); + return 0; + case 'v': + version(); + return 0; + case '?': // getopt 函数提供给用户捕获无效的命令选项 + error("main", "option type check error"); + error("main", "exeute $ tcping6 -h find help information"); + return 1; + } + } + + if ((argc - optind) != 1) { + error("main", "option number check error"); + error("main", "exeute $ tcping6 -h find help information"); + return 1; + } else { + parameter = argv[optind]; + strcpy(ip, parameter); + strcpy(po, parameter); + } + + if (judge_ip(ip, &ipp, &addr6) == 0 || judge_po(po, &pop, &addr6) == 0) { + /* judge_ip() 执行结束时, 字符数组变量 ip 仍然存储完整的目标套接字 (:port 部分被切割) + * judge_po() 执行结束时, 字符数组变量 po 仍然存储完整的目标套接字 + */ + error("main", "option value check error"); + error("main", "exeute $ tcping6 -h find help information"); + return 1; + } + + int ms[count]; + int max_ms = 0; + int min_ms = 0; + int avg_ms = 0; + int counts = 0; // 统计成功次数 (count success) + int counte = 0; // 统计失败次数 (count error) + + for (int i = 0; i < count; i++) { + struct timeval begin; + struct timeval end; + + ms[i] = connect_to(&begin, &end, &addr6); + if (ms[i] == 0) { + counte++; + if (quiet == 0) { + printf("Ping tcp://%s:%s - error\n", ipp, pop); + } + } else { + counts++; + if (quiet == 0) { + printf("Ping tcp://%s:%s - success: timepay %d(ms)\n", ipp, pop, ms[i]); + } + } + sleep(interval); + } + + max_ms = max(ms, count); + min_ms = min(ms, count); + avg_ms = avg(ms, count, counts); + if (quiet == 0) { + printf("\n"); + printf("Tcping6 statistics tcp://%s:%s\n", ipp, pop); + printf(" %d times request success, %d times request error\n", counts, counte); + printf("Approximate trip times:\n"); + printf(" Maximum = %d(ms), Minimum = %d(ms), Average = %d(ms)\n", max_ms, min_ms, avg_ms); + printf("\n"); + } + + if (counte == count) { + return 2; + } + if (counte != 0) { + return 1; + } + return 0; +} + +