diff --git a/.gitignore b/.gitignore
index a7e4258..a87e6df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,45 +1,39 @@
-# ---> Java
-# Compiled class file
-*.class
-
-# Log file
-*.log
-
-# BlueJ files
-*.ctxt
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
-*.jar
-*.war
-*.nar
-*.ear
-*.zip
-*.tar.gz
-*.rar
-
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
-replay_pid*
-
-# ---> Maven
target/
-pom.xml.tag
-pom.xml.releaseBackup
-pom.xml.versionsBackup
-pom.xml.next
-release.properties
-dependency-reduced-pom.xml
-buildNumber.properties
-.mvn/timing.properties
-# https://github.com/takari/maven-wrapper#usage-without-binary-jar
-.mvn/wrapper/maven-wrapper.jar
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
-# Eclipse m2e generated files
-# Eclipse Core
-.project
-# JDT-specific (Eclipse Java Development Tools)
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+.idea/
+
+### Eclipse ###
+.apt_generated
.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
index 6b87d8b..6fa9d23 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,111 @@
-# ddns
+# DNS域名解析工具
+## 说明
+
+该项目通过云服务商提供的API,实现对账号下的域名进行添加,查询,更新,删除操作。
+
+目前仅接入阿里云DNS,可以通过实现 `DnsProvider` 类,扩展其他云服务商。
+
+### 配置
+
+在云服务商后台配置生成授权信息,把 key 和 secret 配置到系统环境变量。
+
+也可以在启动前设置临时变量
+
+- windows
+
+```
+set ALIYUN_ACCESS_KEY_ID=阿里云ACCESS_KEY_ID
+
+set ALIYUN_ACCESS_KEY_SECRET=阿里云ACCESS_KEY_SECRET
+```
+
+- linux
+
+```
+export ALIYUN_ACCESS_KEY_ID=阿里云 ACCESS_KEY_ID
+
+export ALIYUN_ACCESS_KEY_SECRET=阿里云 ACCESS_KEY_SECRET
+```
+
+## 运行
+
+编译完成后会生成可运行的jar包和原生应用
+
+jar包运行(需安装java环境):
+
+```
+java -jar 生成的jar包 [参数1] [值1] [参数2] [值2] 操作
+```
+
+使用原生应用运行不需要单独安装java环境:
+
+```java
+编译后的可执行文件 [参数1] [值1] [参数2] [值2] 操作
+```
+
+参数说明
+
+| 参数 | 备注 |
+| --------------- |---------------------------|
+| provider | 服务提供商,如:aliyun |
+| domain | 域名,如:baidu.com |
+| rr | 记录,和域拼接在一起为完整域名,如:www |
+| type | 解析类型,如:A/AAAA/TXT/CNAME 等 |
+| value | 记录值,如果是A类型则对应IP地址,以此类推 |
+
+操作说明:
+
+| 操作 | 备注 |
+|--------|------|
+| add | 添加域名 |
+| view | 查看域名 |
+| update | 更新域名 |
+| delete | 域除域名 |
+
+## 示例
+
+- 设置临时环境变量
+
+```
+set ALIYUN_ACCESS_KEY_ID=阿里云ACCESS_KEY_ID
+
+set ALIYUN_ACCESS_KEY_SECRET=阿里云ACCESS_KEY_SECRET
+
+set ALIYU_REGION_ID=区域id
+```
+
+> ALIYUN_REGION_ID 非密填,默认 cn-hangzhou
+> 其他区域可参考:https://api.aliyun.com/product/Alidns ,如:cn-shenzhen
+
+- 新增域名
+
+```
+dns.exe -provider aliyun -domain engr-z.com -rr test -type A -value 8.8.8.8 add
+```
+
+
+
+> ipv4是A记录,ipv6是AAAA记录
+
+- 查看域名
+
+```
+dns.exe -provider aliyun -domain engr-z.com -rr test show
+```
+
+- 更新域名
+
+```
+dns.exe -provider aliyun -domain engr-z.com -rr test -type A -value 8.8.4.4 update
+```
+
+
+
+- 删除域名
+
+```
+dns.exe -provider aliyun -domain engr-z.com -rr test delete
+```
+
+
diff --git a/img/img_add.png b/img/img_add.png
new file mode 100644
index 0000000..4bb114b
Binary files /dev/null and b/img/img_add.png differ
diff --git a/img/img_delete.png b/img/img_delete.png
new file mode 100644
index 0000000..d6e812f
Binary files /dev/null and b/img/img_delete.png differ
diff --git a/img/img_update.png b/img/img_update.png
new file mode 100644
index 0000000..a501039
Binary files /dev/null and b/img/img_update.png differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..3ca1f45
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,147 @@
+
+
+ 4.0.0
+
+ com.z.toys
+ z-dns
+ 1.0-SNAPSHOT
+
+
+ 17
+ ${java.version}
+ ${java.version}
+ com.z.toys.dns.Main
+ 1.18.34
+ 2.0.13
+ 1.8.0
+ 3.4.0
+ 0.3.3
+ 24.0.1
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j.version}
+
+
+ commons-cli
+ commons-cli
+ ${commons-cli.version}
+
+
+ com.aliyun
+ alidns20150109
+ ${alidns.version}
+
+
+ com.aliyun
+ credentials-java
+ ${aliyun-credentials-java.version}
+
+
+ org.graalvm.nativeimage
+ svm
+ ${svm.version}
+ provided
+
+
+
+
+
+ native
+
+ true
+
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+
+
+
+ compile
+
+ package
+
+
+
+ false
+ ${project.artifactId}-${project.version}-native
+
+ --no-fallback
+ --initialize-at-build-time=org.slf4j
+ --enable-http
+ --enable-https
+ -H:+ReportExceptionStackTraces
+ -H:+ReportUnsupportedElementsAtRuntime
+ -H:+AddAllCharsets
+
+ ${start-class}
+
+
+
+
+
+
+ single
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+ ${project.artifactId}-${project.version}-single
+
+
+ ${start-class}
+
+
+
+ jar-with-dependencies
+
+ false
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
+
+
+ ${artifactId}-${project.version}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/z/toys/dns/Main.java b/src/main/java/com/z/toys/dns/Main.java
new file mode 100644
index 0000000..09bbd04
--- /dev/null
+++ b/src/main/java/com/z/toys/dns/Main.java
@@ -0,0 +1,140 @@
+package com.z.toys.dns;
+
+import com.aliyun.tea.TeaException;
+import com.z.toys.dns.model.ParamModel;
+import com.z.toys.dns.provider.DnsProvider;
+import com.z.toys.dns.provider.DnsProviderManage;
+import com.z.toys.dns.util.BeanUtil;
+import com.z.toys.dns.util.StrUtil;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.cli.*;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author wangzz
+ * @since 2024/3/26
+ **/
+@Slf4j(topic = "DnsMain")
+public class Main {
+
+ /**
+ *
+ * @param args
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ @SneakyThrows
+ public static void main(String[] args) {
+
+ try {
+
+ CommandLineParser parser = new DefaultParser();
+ CommandLine cmd = parser.parse(getOptions(), args);
+
+ String op = args[args.length - 1];
+ if (StrUtil.isEmpty(op)) {
+ log.error("操作参数不能为空,add(新增)/show(查询显示)/update(更新)/delete(删除)");
+ return;
+ }
+
+ Map map = Arrays.stream(cmd.getOptions()).collect(Collectors.toMap(opt -> opt.getOpt(), opt -> opt.getValue()));
+ log.debug("参数:{}", map);
+
+ ParamModel paramModel = BeanUtil.map2Bean(map, ParamModel.class);
+
+ DnsProvider dnsProvider = DnsProviderManage.getProvider(paramModel.getProvider());
+ if (Objects.isNull(dnsProvider)) {
+ log.error("provider无效:{}", paramModel.getProvider());
+ return;
+ }
+
+ switch (op) {
+ case "show": {
+ dnsProvider.printDomainInfo(paramModel);
+ break;
+ }
+ case "add": {
+ dnsProvider.addDomain(paramModel);
+ break;
+ }
+ case "update": {
+ dnsProvider.updateDomain(paramModel);
+ break;
+ }
+ case "delete": {
+ dnsProvider.deleteDomain(paramModel);
+ break;
+ }
+ default:
+ log.error("操作参数错误:{}", op);
+ }
+
+ } catch (ParseException e) {
+ log.error("参数解析错误", e);
+
+ } catch (TeaException e) {
+ log.error("TeaException", e);
+ }
+
+ }
+
+
+ /**
+ * 参数
+ * @return
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ public static Options getOptions() {
+
+ Options options = new Options();
+
+ Option provider = Option.builder()
+ .option("provider")
+ .hasArg()
+ .required(true)
+ .desc("dns提供商,如:aliyun")
+ .build();
+ options.addOption(provider);
+
+ Option domain = Option.builder()
+ .option("domain")
+ .hasArg()
+ .required(true)
+ .desc("域名,如:engr-z.com")
+ .build();
+ options.addOption(domain);
+
+ Option rr = Option.builder()
+ .option("rr")
+ .hasArg()
+ .required(true)
+ .desc("记录/子域名")
+ .build();
+ options.addOption(rr);
+
+ Option type = Option.builder()
+ .option("type")
+ .hasArg()
+ .required(false)
+ .desc("记录类型,如:A/AAAA")
+ .build();
+ options.addOption(type);
+
+ Option value = Option.builder()
+ .option("value")
+ .hasArg()
+ .required(false)
+ .desc("记录值")
+ .build();
+ options.addOption(value);
+
+ return options;
+ }
+
+}
diff --git a/src/main/java/com/z/toys/dns/model/ParamModel.java b/src/main/java/com/z/toys/dns/model/ParamModel.java
new file mode 100644
index 0000000..6b88e03
--- /dev/null
+++ b/src/main/java/com/z/toys/dns/model/ParamModel.java
@@ -0,0 +1,37 @@
+package com.z.toys.dns.model;
+
+import lombok.Data;
+
+/**
+ * @author wangzz
+ * @since 2024/3/26
+ **/
+@Data
+public class ParamModel {
+
+ /**
+ * dns供应商
+ */
+ private String provider;
+
+ /**
+ * 域名
+ */
+ private String domain;
+
+ /**
+ * 记录/子域名
+ */
+ private String rr;
+
+ /**
+ * 类型
+ */
+ private String type;
+
+ /**
+ * 记录值
+ */
+ private String value;
+
+}
diff --git a/src/main/java/com/z/toys/dns/provider/AliDnsProvider.java b/src/main/java/com/z/toys/dns/provider/AliDnsProvider.java
new file mode 100644
index 0000000..95b471f
--- /dev/null
+++ b/src/main/java/com/z/toys/dns/provider/AliDnsProvider.java
@@ -0,0 +1,175 @@
+package com.z.toys.dns.provider;
+
+import com.aliyun.alidns20150109.Client;
+import com.aliyun.alidns20150109.models.*;
+import com.aliyun.tea.TeaModel;
+import com.z.toys.dns.model.ParamModel;
+import com.z.toys.dns.util.StrUtil;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author wangzz
+ * @since 2024/3/26
+ **/
+@Slf4j(topic = "AliDnsProvider")
+public class AliDnsProvider implements DnsProvider {
+
+ /**
+ * 初使化账号
+ * @return
+ * @throws Exception
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ public static Client createClient() throws Exception {
+ String accessKeyId = System.getenv("ALIYUN_ACCESS_KEY_ID");
+ String keySecret = System.getenv("ALIYUN_ACCESS_KEY_SECRET");
+ if (StrUtil.isEmpty(accessKeyId) || StrUtil.isEmpty(keySecret)) {
+ throw new RuntimeException("阿里云 accessKeyId 或 keySecret 未配置");
+ }
+
+ // Endpoint 规则为:[product_code].[regionid].aliyuncs.com
+ // 请参考 https://api.aliyun.com/product/Alidns
+ String endpoint = String.format("alidns.%s.aliyuncs.com", Objects.toString(System.getenv("ALIYUN_REGION_ID"), "cn-hangzhou"));
+ com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
+ .setAccessKeyId(accessKeyId)
+ .setAccessKeySecret(keySecret)
+ .setEndpoint(endpoint);
+
+ return new Client(config);
+ }
+
+ @SneakyThrows
+ public DescribeDomainRecordsResponseBody.DescribeDomainRecordsResponseBodyDomainRecordsRecord getDomainInfo(ParamModel param) {
+
+ Client client = createClient();
+ DescribeDomainRecordsRequest req = new DescribeDomainRecordsRequest();
+ req.setDomainName(param.getDomain());
+ req.setRRKeyWord(param.getRr());
+
+ log.debug("查询域名解析记录请求:{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(req)));
+ DescribeDomainRecordsResponse resp = client.describeDomainRecords(req);
+ log.debug("查询域名解析记录返回:{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(resp)));
+
+ List records = resp.getBody().getDomainRecords().getRecord();
+ if (Objects.nonNull(records) && !records.isEmpty()) {
+ return records.get(0);
+ }
+
+ return null;
+ }
+
+ @SneakyThrows
+ @Override
+ public void printDomainInfo(ParamModel param) {
+
+ if (StrUtil.isEmpty(param.getDomain()) || StrUtil.isEmpty(param.getRr())) {
+ log.error("必要参数不能为空");
+ return;
+ }
+
+ DescribeDomainRecordsResponseBody.DescribeDomainRecordsResponseBodyDomainRecordsRecord record = this.getDomainInfo(param);
+ if (Objects.nonNull(record)) {
+ log.info("查询域名结果↓");
+ log.info("\t recordId: {}", record.getRecordId());
+ log.info("\tdomainName: {}", record.getDomainName());
+ log.info("\t rr: {}", record.getRR());
+ log.info("\t type: {}", record.getType());
+ log.info("\t value: {}", record.getValue());
+ log.info("\t ttl: {}", record.getTTL());
+ log.info("\t remark: {}", Objects.toString(record.getRemark(), ""));
+ } else {
+ log.info("域名未找到");
+ }
+
+ }
+
+ @SneakyThrows
+ @Override
+ public void addDomain(ParamModel param) {
+
+ if (StrUtil.isEmpty(param.getDomain()) || StrUtil.isEmpty(param.getRr()) || StrUtil.isEmpty(param.getType()) || StrUtil.isEmpty(param.getValue())) {
+ log.error("必要参数不能为空");
+ return;
+ }
+
+ DescribeDomainRecordsResponseBody.DescribeDomainRecordsResponseBodyDomainRecordsRecord record = this.getDomainInfo(param);
+ if (Objects.isNull(record)) {
+ AddDomainRecordRequest req = new AddDomainRecordRequest();
+ req.setDomainName(param.getDomain());
+ req.setRR(param.getRr());
+ req.setType(param.getType());
+ req.setValue(param.getValue());
+ log.debug("添加域名解析记录请求:{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(req)));
+ AddDomainRecordResponse resp = createClient().addDomainRecord(req);
+ log.debug("添加域名解析记录返回:{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(resp)));
+
+ log.info("添加域名解析记录成功:recordId={}", resp.getBody().getRecordId());
+
+ } else {
+ log.info("域名记录已存在", param.getRr());
+ }
+ }
+
+ @SneakyThrows
+ @Override
+ public void updateDomain(ParamModel param) {
+
+ if (StrUtil.isEmpty(param.getDomain()) || StrUtil.isEmpty(param.getRr()) || StrUtil.isEmpty(param.getType()) || StrUtil.isEmpty(param.getValue())) {
+ log.error("必要参数不能为空");
+ return;
+ }
+
+ DescribeDomainRecordsResponseBody.DescribeDomainRecordsResponseBodyDomainRecordsRecord record = this.getDomainInfo(param);
+ if (Objects.nonNull(record)) {
+
+ if (Objects.equals(param.getValue(), record.getValue())) {
+ log.info("域名解析记录值相同,不更新");
+ return;
+ }
+
+ UpdateDomainRecordRequest req = new UpdateDomainRecordRequest();
+ req.setRecordId(record.getRecordId());
+ req.setRR(param.getRr());
+ req.setType(param.getType());
+ req.setValue(param.getValue());
+ log.debug("更新域名解析记录请求:{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(req)));
+ UpdateDomainRecordResponse resp = createClient().updateDomainRecord(req);
+ log.debug("更新域名解析记录返回:{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(resp)));
+
+ log.info("更新域名解析记录成功");
+
+ } else {
+ log.info("域名未找到");
+ }
+ }
+
+ @SneakyThrows
+ @Override
+ public void deleteDomain(ParamModel param) {
+
+ if (StrUtil.isEmpty(param.getDomain()) || StrUtil.isEmpty(param.getRr())) {
+ log.error("必要参数不能为空");
+ return;
+ }
+
+ DescribeDomainRecordsResponseBody.DescribeDomainRecordsResponseBodyDomainRecordsRecord record = this.getDomainInfo(param);
+ if (Objects.nonNull(record)) {
+
+ DeleteDomainRecordRequest req = new DeleteDomainRecordRequest();
+ req.setRecordId(record.getRecordId());
+ log.debug("删除域名解析记录请求:{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(req)));
+ DeleteDomainRecordResponse resp = createClient().deleteDomainRecord(req);
+ log.debug("删除域名解析记录返回:{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(resp)));
+
+ log.info("删除域名解析记录成功:recordId={},domain={},rr={}", record.getRecordId(), param.getDomain(), param.getRr());
+
+ } else {
+ log.info("域名未找到");
+ }
+ }
+}
diff --git a/src/main/java/com/z/toys/dns/provider/DnsProvider.java b/src/main/java/com/z/toys/dns/provider/DnsProvider.java
new file mode 100644
index 0000000..034c631
--- /dev/null
+++ b/src/main/java/com/z/toys/dns/provider/DnsProvider.java
@@ -0,0 +1,45 @@
+package com.z.toys.dns.provider;
+
+import com.z.toys.dns.model.ParamModel;
+
+/**
+ * @author wangzz
+ * @since 2024/3/26
+ **/
+public interface DnsProvider {
+
+ String ALIYUN = "aliyun";
+
+ /**
+ * 打印域名信息
+ * @param param
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ void printDomainInfo(ParamModel param);
+
+ /**
+ * 添加域名
+ * @param param
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ void addDomain(ParamModel param);
+
+ /**
+ * 更新域名
+ * @param param
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ void updateDomain(ParamModel param);
+
+ /**
+ * 删除域名
+ * @param param
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ void deleteDomain(ParamModel param);
+
+}
diff --git a/src/main/java/com/z/toys/dns/provider/DnsProviderManage.java b/src/main/java/com/z/toys/dns/provider/DnsProviderManage.java
new file mode 100644
index 0000000..220c727
--- /dev/null
+++ b/src/main/java/com/z/toys/dns/provider/DnsProviderManage.java
@@ -0,0 +1,25 @@
+package com.z.toys.dns.provider;
+
+/**
+ * @author wangzz
+ * @since 2024/3/26
+ **/
+public class DnsProviderManage {
+
+ /**
+ * 获取dns内容提供者
+ * @param provider
+ * @return
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ public static DnsProvider getProvider(String provider) {
+
+ switch (provider) {
+ case DnsProvider.ALIYUN:
+ return new AliDnsProvider();
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/z/toys/dns/util/BeanUtil.java b/src/main/java/com/z/toys/dns/util/BeanUtil.java
new file mode 100644
index 0000000..acf7181
--- /dev/null
+++ b/src/main/java/com/z/toys/dns/util/BeanUtil.java
@@ -0,0 +1,74 @@
+package com.z.toys.dns.util;
+
+import lombok.SneakyThrows;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author wangzz
+ * @since 2024/3/26
+ **/
+public class BeanUtil {
+
+ /**
+ * 将 Bean 转换为 Map
+ * @param bean
+ * @return
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ @SneakyThrows
+ public static Map bean2Map(Object bean) {
+
+ Map map = new HashMap<>();
+ BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
+ PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
+ for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+ map.put(propertyDescriptor.getName(), propertyDescriptor.getReadMethod().invoke(bean));
+ }
+
+ return map;
+ }
+
+ /**
+ * 将 Map 转换为 Bean
+ * @param map
+ * @param clazz
+ * @return
+ * @param
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ @SneakyThrows
+ public static T map2Bean(Map map, Class clazz) {
+
+ // 获取 Bean 的类信息
+ BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
+
+ // 获取 Bean 的属性描述符
+ PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
+
+ // 创建 Bean 实例
+ T bean = clazz.newInstance();
+
+ // 遍历属性描述符
+ for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+ // 获取属性名称
+ String name = propertyDescriptor.getName();
+ // 获取属性值
+ Object value = map.get(name);
+ // 如果属性值不为空,则将属性值设置到 Bean 中
+ if (value != null) {
+ propertyDescriptor.getWriteMethod().invoke(bean, value);
+ }
+ }
+
+ return bean;
+ }
+
+
+}
diff --git a/src/main/java/com/z/toys/dns/util/Okhttp3_Internal_Util.java b/src/main/java/com/z/toys/dns/util/Okhttp3_Internal_Util.java
new file mode 100644
index 0000000..518f669
--- /dev/null
+++ b/src/main/java/com/z/toys/dns/util/Okhttp3_Internal_Util.java
@@ -0,0 +1,50 @@
+package com.z.toys.dns.util;
+
+import com.oracle.svm.core.annotate.Alias;
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import okhttp3.internal.Util;
+import okio.BufferedSource;
+import okio.ByteString;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+/**
+ * @author wangzz
+ * @since 2024/3/26
+ **/
+@TargetClass(Util.class)
+public final class Okhttp3_Internal_Util {
+
+ @Alias
+ private static ByteString UTF_8_BOM;
+ @Alias
+ private static ByteString UTF_16_BE_BOM;
+ @Alias
+ private static ByteString UTF_16_LE_BOM;
+ @Alias
+ public static Charset UTF_8;
+ @Alias
+ private static Charset UTF_16_BE;
+ @Alias
+ private static Charset UTF_16_LE;
+
+ @Substitute
+ public static Charset bomAwareCharset(BufferedSource source, Charset charset) throws IOException {
+
+ if (source.rangeEquals(0, UTF_8_BOM)) {
+ source.skip(UTF_8_BOM.size());
+ return UTF_8;
+ }
+ if (source.rangeEquals(0, UTF_16_BE_BOM)) {
+ source.skip(UTF_16_BE_BOM.size());
+ return UTF_16_BE;
+ }
+ if (source.rangeEquals(0, UTF_16_LE_BOM)) {
+ source.skip(UTF_16_LE_BOM.size());
+ return UTF_16_LE;
+ }
+ return charset;
+ }
+}
diff --git a/src/main/java/com/z/toys/dns/util/StrUtil.java b/src/main/java/com/z/toys/dns/util/StrUtil.java
new file mode 100644
index 0000000..c1cc68b
--- /dev/null
+++ b/src/main/java/com/z/toys/dns/util/StrUtil.java
@@ -0,0 +1,35 @@
+package com.z.toys.dns.util;
+
+import java.util.Objects;
+
+/**
+ * @author wangzz
+ * @since 2024/3/26
+ **/
+public class StrUtil {
+
+ /**
+ *
+ * @param str
+ * @return
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ public static boolean isEmpty(String str) {
+
+ return Objects.isNull(str) || "".equals(str);
+ }
+
+ /**
+ *
+ * @param str
+ * @return
+ * @author wangzz
+ * @since 2024/3/26
+ */
+ public static boolean isNotEmpty(String str) {
+
+ return !isEmpty(str);
+ }
+
+}
diff --git a/src/main/resources/META-INF/native-image/fix_okhttp3.json b/src/main/resources/META-INF/native-image/fix_okhttp3.json
new file mode 100644
index 0000000..a893bc8
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/fix_okhttp3.json
@@ -0,0 +1,12 @@
+[
+ {
+ "annotatedClass": "com.z.toys.dns.util.Okhttp3_Internal_Util",
+ "originalClass": "okhttp3.internal.Util",
+ "methods": [
+ {
+ "annotatedName": "bomAwareCharset",
+ "substitute": true
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/src/main/resources/META-INF/native-image/jni-config.json b/src/main/resources/META-INF/native-image/jni-config.json
new file mode 100644
index 0000000..8b4e417
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/jni-config.json
@@ -0,0 +1,6 @@
+[
+{
+ "name":"java.lang.Boolean",
+ "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
+}
+]
diff --git a/src/main/resources/META-INF/native-image/predefined-classes-config.json b/src/main/resources/META-INF/native-image/predefined-classes-config.json
new file mode 100644
index 0000000..0e79b2c
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/predefined-classes-config.json
@@ -0,0 +1,8 @@
+[
+ {
+ "type":"agent-extracted",
+ "classes":[
+ ]
+ }
+]
+
diff --git a/src/main/resources/META-INF/native-image/proxy-config.json b/src/main/resources/META-INF/native-image/proxy-config.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/proxy-config.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/main/resources/META-INF/native-image/reflect-config.json b/src/main/resources/META-INF/native-image/reflect-config.json
new file mode 100644
index 0000000..5451f2d
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/reflect-config.json
@@ -0,0 +1,344 @@
+[
+{
+ "name":"[B"
+},
+{
+ "name":"[Ljava.lang.String;"
+},
+{
+ "name":"[Lsun.security.pkcs.SignerInfo;"
+},
+{
+ "name":"com.aliyun.alidns20150109.models.AddDomainRecordRequest",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.alidns20150109.models.AddDomainRecordResponse",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.alidns20150109.models.AddDomainRecordResponseBody",
+ "allPublicFields":true,
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.aliyun.alidns20150109.models.DescribeDomainRecordsRequest",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.alidns20150109.models.DescribeDomainRecordsResponse",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.alidns20150109.models.DescribeDomainRecordsResponseBody",
+ "allPublicFields":true,
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.aliyun.alidns20150109.models.UpdateDomainRecordRequest",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.alidns20150109.models.UpdateDomainRecordResponse",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.alidns20150109.models.UpdateDomainRecordResponseBody",
+ "allPublicFields":true,
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.aliyun.alidns20150109.models.DeleteDomainRecordRequest",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.alidns20150109.models.DeleteDomainRecordResponse",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.alidns20150109.models.DeleteDomainRecordResponseBody",
+ "allPublicFields":true,
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.aliyun.alidns20150109.models.DescribeDomainRecordsResponseBody$DescribeDomainRecordsResponseBodyDomainRecords",
+ "allPublicFields":true,
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.aliyun.alidns20150109.models.DescribeDomainRecordsResponseBody$DescribeDomainRecordsResponseBodyDomainRecordsRecord",
+ "allPublicFields":true,
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.aliyun.credentials.models.Config",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.teaopenapi.models.OpenApiRequest",
+ "allPublicFields":true
+},
+{
+ "name":"com.aliyun.teaopenapi.models.Params",
+ "allPublicFields":true
+},
+{
+ "name":"com.z.toys.dns.model.ParamModel",
+ "queryAllPublicMethods":true,
+ "methods":[{"name":"","parameterTypes":[] }, {"name":"setDomain","parameterTypes":["java.lang.String"] }, {"name":"setProvider","parameterTypes":["java.lang.String"] }, {"name":"setRr","parameterTypes":["java.lang.String"] }, {"name":"setType","parameterTypes":["java.lang.String"] }, {"name":"setValue","parameterTypes":["java.lang.String"] }]
+},
+{
+ "name":"com.z.toys.dns.model.ParamModelBeanInfo"
+},
+{
+ "name":"com.z.toys.dns.model.ParamModelCustomizer"
+},
+{
+ "name":"com.sun.crypto.provider.AESCipher$General",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.ARCFOURCipher",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.DESCipher",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.DESedeCipher",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.DHParameters",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.HmacCore$HmacSHA256",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.HmacCore$HmacSHA384",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"com.sun.crypto.provider.TlsMasterSecretGenerator",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"java.beans.PropertyVetoException"
+},
+{
+ "name":"java.lang.Object",
+ "queryAllPublicMethods":true
+},
+{
+ "name":"java.lang.ObjectBeanInfo"
+},
+{
+ "name":"java.lang.ObjectCustomizer"
+},
+{
+ "name":"java.lang.String"
+},
+{
+ "name":"java.lang.Thread",
+ "fields":[{"name":"threadLocalRandomProbe"}]
+},
+{
+ "name":"java.lang.Throwable",
+ "methods":[{"name":"addSuppressed","parameterTypes":["java.lang.Throwable"] }]
+},
+{
+ "name":"java.lang.reflect.AccessibleObject",
+ "fields":[{"name":"override"}]
+},
+{
+ "name":"java.security.AlgorithmParametersSpi"
+},
+{
+ "name":"java.security.KeyStoreSpi"
+},
+{
+ "name":"java.security.SecureRandomParameters"
+},
+{
+ "name":"java.security.interfaces.ECPrivateKey"
+},
+{
+ "name":"java.security.interfaces.ECPublicKey"
+},
+{
+ "name":"java.sql.Date"
+},
+{
+ "name":"java.util.ArrayList",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"java.util.Date"
+},
+{
+ "name":"java.util.HashMap",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"java.util.concurrent.atomic.Striped64",
+ "fields":[{"name":"base"}, {"name":"cellsBusy"}]
+},
+{
+ "name":"javax.net.ssl.SSLParameters",
+ "methods":[{"name":"setApplicationProtocols","parameterTypes":["java.lang.String[]"] }]
+},
+{
+ "name":"javax.net.ssl.SSLSocket",
+ "methods":[{"name":"getApplicationProtocol","parameterTypes":[] }]
+},
+{
+ "name":"javax.security.auth.x500.X500Principal",
+ "fields":[{"name":"thisX500Name"}],
+ "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }]
+},
+{
+ "name":"sun.misc.Unsafe",
+ "fields":[{"name":"theUnsafe"}]
+},
+{
+ "name":"sun.security.pkcs12.PKCS12KeyStore",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.DRBG",
+ "methods":[{"name":"","parameterTypes":["java.security.SecureRandomParameters"] }]
+},
+{
+ "name":"sun.security.provider.DSA$SHA224withDSA",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.DSA$SHA256withDSA",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.JavaKeyStore$JKS",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.MD5",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.SHA",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.SHA2$SHA224",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.SHA2$SHA256",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.SHA5$SHA384",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.SHA5$SHA512",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.provider.X509Factory",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.rsa.PSSParameters",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.rsa.RSAKeyFactory$Legacy",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.rsa.RSAPSSSignature",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.rsa.RSASignature$SHA224withRSA",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.ssl.SSLContextImpl$TLSContext",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory",
+ "methods":[{"name":"","parameterTypes":[] }]
+},
+{
+ "name":"sun.security.util.ObjectIdentifier"
+},
+{
+ "name":"sun.security.x509.AuthorityInfoAccessExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.AuthorityKeyIdentifierExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.BasicConstraintsExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.CRLDistributionPointsExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.CertificateExtensions"
+},
+{
+ "name":"sun.security.x509.CertificatePoliciesExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.ExtendedKeyUsageExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.IssuerAlternativeNameExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.KeyUsageExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.NetscapeCertTypeExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.PrivateKeyUsageExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.SubjectAlternativeNameExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+},
+{
+ "name":"sun.security.x509.SubjectKeyIdentifierExtension",
+ "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
+}
+]
diff --git a/src/main/resources/META-INF/native-image/resource-config.json b/src/main/resources/META-INF/native-image/resource-config.json
new file mode 100644
index 0000000..b643d8d
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/resource-config.json
@@ -0,0 +1,13 @@
+{
+ "resources":{
+ "includes":[{
+ "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E"
+ }, {
+ "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt67b/nfkc.nrm\\E"
+ }, {
+ "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt67b/uprops.icu\\E"
+ }, {
+ "pattern":"java.base:\\Qsun/net/idn/uidna.spp\\E"
+ }]},
+ "bundles":[]
+}
diff --git a/src/main/resources/META-INF/native-image/serialization-config.json b/src/main/resources/META-INF/native-image/serialization-config.json
new file mode 100644
index 0000000..f3d7e06
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/serialization-config.json
@@ -0,0 +1,8 @@
+{
+ "types":[
+ ],
+ "lambdaCapturingTypes":[
+ ],
+ "proxies":[
+ ]
+}