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;
+}
+
+